Imported Upstream version 3.3.3
[debian/amanda] / amar-src / amar-test.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27
28 #include "amanda.h"
29 #include "amar.h"
30 #include "testutils.h"
31 #include "simpleprng.h"
32
33 static char *temp_filename = NULL;
34
35 /****
36  * Macros for creating files with a particular structure
37  */
38
39 #define WRITE_HEADER(fd, version) do { \
40     char hdr[28]; \
41     bzero(hdr, 28); \
42     snprintf(hdr, 28, "AMANDA ARCHIVE FORMAT %d", (version)); \
43     g_assert(full_write((fd), hdr, 28) == 28); \
44 } while(0);
45
46 #define WRITE_RECORD(fd, filenum, attrid, size, eoa, data) do { \
47     struct { uint16_t f; uint16_t a; uint32_t s; } rec; \
48     rec.f = htons((filenum)); \
49     rec.a = htons((attrid)); \
50     rec.s = htonl((size) | (eoa? 0x80000000 : 0)); \
51     g_assert(full_write((fd), &rec, sizeof(rec)) == sizeof(rec)); \
52     g_assert(full_write((fd), (data), (size)) == (size)); \
53 } while(0);
54
55 #define WRITE_RECORD_STR(fd, filenum, attrid, eoa, str) do { \
56     size_t len = strlen((str)); \
57     WRITE_RECORD((fd), (filenum), (attrid), len, (eoa), (str)); \
58 } while(0);
59
60 /****
61  * Assertions for amanda_read_archive callbacks
62  */
63
64 typedef enum {
65     EXP_END,
66     EXP_START_FILE,
67     EXP_ATTRDATA,
68     EXP_FINISH_FILE,
69 } expected_kind_t;
70
71 typedef struct {
72     expected_kind_t kind;
73     uint16_t filenum;
74     uint16_t attrid;
75     char *data;
76     size_t datasize;
77     gboolean multipart_ok;
78     gboolean eoa;
79     gboolean truncated;
80     gboolean should_ignore;
81     gboolean isstr;
82 } expected_step_t;
83
84 typedef struct {
85     expected_step_t *steps;
86     int curstep;
87 } expected_state_t;
88
89 #define EXPECT_START_FILE(filenum, data, datasize, should_ignore) \
90     { EXP_START_FILE, (filenum), 0, (data), (datasize), 0, 0, 0, (should_ignore), 0 }
91
92 #define EXPECT_START_FILE_STR(filenum, filename, should_ignore) \
93     { EXP_START_FILE, (filenum), 0, (filename), strlen((filename)), 0, 0, 0, (should_ignore), 1 }
94
95 #define EXPECT_ATTR_DATA(filenum, attrid, data, datasize, eoa, truncated) \
96     { EXP_ATTRDATA, (filenum), (attrid), (data), (datasize), 0, (eoa), (truncated), 0, 0 }
97
98 #define EXPECT_ATTR_DATA_MULTIPART(filenum, attrid, data, datasize, eoa, truncated) \
99     { EXP_ATTRDATA, (filenum), (attrid), (data), (datasize), 1, (eoa), (truncated), 0, 0 }
100
101 #define EXPECT_ATTR_DATA_STR(filenum, attrid, datastr, eoa, truncated) \
102     { EXP_ATTRDATA, (filenum), (attrid), (datastr), strlen((datastr)), 0, (eoa), (truncated), 0, 1 }
103
104 #define EXPECT_FINISH_FILE(filenum, truncated) \
105     { EXP_FINISH_FILE, (filenum), 0, 0, 0, 0, 0, (truncated), 0, 0 }
106
107 #define EXPECT_END() \
108     { EXP_END, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
109
110 #define EXPECT_FAILURE(fmt, ...) do { \
111     fprintf(stderr, fmt "\n", __VA_ARGS__); \
112     exit(1); \
113 } while(0)
114
115 static gboolean
116 file_start_cb(
117         gpointer user_data,
118         uint16_t filenum,
119         gpointer filename,
120         gsize filename_len,
121         gboolean *ignore,
122         gpointer *file_data G_GNUC_UNUSED)
123 {
124     expected_state_t *state = user_data;
125     expected_step_t *step = state->steps + state->curstep;
126
127     tu_dbg("file_start_cb(NULL, %d, '%s', %zd, .., ..)\n",
128         (int)filenum, (char *)filename, filename_len);
129
130     if (step->kind != EXP_START_FILE)
131         EXPECT_FAILURE("step %d: unexpected new file with fileid %d",
132                         state->curstep, (int)filenum);
133
134     if (step->filenum != filenum)
135         EXPECT_FAILURE("step %d: expected new file with filenum %d; got filenum %d",
136                         state->curstep, (int)step->filenum, (int)filenum);
137
138     if (filename_len != step->datasize)
139         EXPECT_FAILURE("step %d: filename lengths do not match: got %zd, expected %zd",
140                         state->curstep, filename_len, step->datasize);
141
142     if (memcmp(filename, step->data, filename_len)) {
143         if (step->isstr) {
144             EXPECT_FAILURE("step %d: new file's filename does not match: got '%*s', expected '%*s'",
145                             state->curstep, (int)filename_len, (char *)filename,
146                             (int)step->datasize, (char *)step->data);
147         } else {
148             EXPECT_FAILURE("step %d: new file's filename does not match",
149                             state->curstep);
150         }
151     }
152
153     *ignore = step->should_ignore;
154     state->curstep++;
155
156     return TRUE;
157 }
158
159 static gboolean
160 file_finish_cb(
161         gpointer user_data,
162         uint16_t filenum,
163         gpointer *file_data G_GNUC_UNUSED,
164         gboolean truncated)
165 {
166     expected_state_t *state = user_data;
167     expected_step_t *step = state->steps + state->curstep;
168
169     tu_dbg("file_finish_cb(NULL, %d, NULL, %d)\n",
170         (int)filenum, truncated);
171
172     if (step->kind != EXP_FINISH_FILE)
173         EXPECT_FAILURE("step %d: unexpected file finish with fileid %d",
174                         state->curstep, (int)filenum);
175
176     if (step->truncated && !truncated)
177         EXPECT_FAILURE("step %d: file %d was unexpectedly not truncated",
178                         state->curstep, (int)filenum);
179
180     if (step->truncated && !truncated)
181         EXPECT_FAILURE("step %d: file %d was unexpectedly truncated",
182                         state->curstep, (int)filenum);
183
184     state->curstep++;
185
186     return TRUE;
187 }
188
189 static gboolean
190 frag_cb(
191         gpointer user_data,
192         uint16_t filenum,
193         gpointer file_data G_GNUC_UNUSED,
194         uint16_t attrid,
195         gpointer attrid_data G_GNUC_UNUSED,
196         gpointer *attr_data G_GNUC_UNUSED,
197         gpointer data,
198         gsize datasize,
199         gboolean eoa,
200         gboolean truncated)
201 {
202     expected_state_t *state = user_data;
203     expected_step_t *step = state->steps + state->curstep;
204
205     tu_dbg("file_finish_cb(NULL, %d, NULL, %d, %p, %zd, %d, %d)\n",
206         (int)filenum, (int)attrid, data, datasize, eoa, truncated);
207
208     if (step->kind != EXP_ATTRDATA)
209         EXPECT_FAILURE("step %d: unexpected attribute data with fileid %d, attrid %d",
210                         state->curstep, (int)filenum, (int)attrid);
211
212     if (step->filenum != filenum)
213         EXPECT_FAILURE("step %d: expected attribute data with filenum %d; got filenum %d",
214                         state->curstep, (int)step->filenum, (int)filenum);
215
216     if (step->attrid != attrid)
217         EXPECT_FAILURE("step %d: expected attribute data with attrid %d; got attrid %d",
218                         state->curstep, (int)step->attrid, (int)attrid);
219
220     /* if we're accepting multiple fragments of the attribute here (due to internal
221      * buffering by the reader), then handle that specially */
222     if (step->multipart_ok && datasize < step->datasize) {
223         if (eoa)
224             EXPECT_FAILURE("step %d: file %d attribute %d: early EOA in multipart attribute",
225                             state->curstep, (int)filenum, (int)attrid);
226
227         if (memcmp(data, step->data, datasize)) {
228             EXPECT_FAILURE("step %d: attribute's data does not match",
229                             state->curstep);
230         }
231         step->data += datasize;
232         step->datasize -= datasize;
233         return TRUE;
234     }
235
236     if (step->eoa && !eoa)
237         EXPECT_FAILURE("step %d: file %d attribute %d: expected EOA did not appear",
238                         state->curstep, (int)filenum, (int)attrid);
239
240     if (!step->eoa && eoa)
241         EXPECT_FAILURE("step %d: file %d attribute %d: unexpected EOA",
242                         state->curstep, (int)filenum, (int)attrid);
243
244     if (!step->truncated && truncated)
245         EXPECT_FAILURE("step %d: file %d attribute %d was unexpectedly truncated",
246                         state->curstep, (int)filenum, (int)attrid);
247
248     if (step->truncated && !truncated)
249         EXPECT_FAILURE("step %d: file %d attribute %d was unexpectedly not truncated",
250                         state->curstep, (int)filenum, (int)attrid);
251
252     if (datasize != step->datasize)
253         EXPECT_FAILURE("step %d: file %d attribute %d lengths do not match: "
254                        "got %zd, expected %zd",
255                         state->curstep, (int)filenum, (int)attrid,
256                         datasize, step->datasize);
257
258     if (memcmp(data, step->data, datasize)) {
259         if (step->isstr) {
260             EXPECT_FAILURE("step %d: attribute's data does not match: got '%*s', expected '%*s'",
261                             state->curstep, (int)datasize, (char *)data,
262                             (int)step->datasize, (char *)step->data);
263         } else {
264             EXPECT_FAILURE("step %d: attribute's data does not match",
265                             state->curstep);
266         }
267     }
268
269     state->curstep++;
270
271     return TRUE;
272 }
273
274 /****
275  * Utilities
276  */
277
278 static int
279 open_temp(gboolean write)
280 {
281     int fd = open(temp_filename, write? O_WRONLY|O_CREAT|O_TRUNC : O_RDONLY, 0777);
282     if (fd < 0) {
283         perror("open temporary file");
284         exit(1);
285     }
286
287     return fd;
288 }
289
290 static void
291 check_gerror_(
292     gboolean ok,
293     GError *error,
294     const char *fn)
295 {
296     if (ok && !error)
297         return;
298
299     if (ok)
300         EXPECT_FAILURE(
301                 "'%s' set 'error' but did not indicate an error condition: %s (%s)\n",
302                 fn, error->message, strerror(error->code));
303     else if (!error)
304         EXPECT_FAILURE(
305                 "'%s' indicated an error condition but did not set 'error'.\n", fn);
306     else
307         EXPECT_FAILURE(
308                 "'%s' error: %s (%s)\n", fn, error->message, strerror(error->code));
309
310     exit(1);
311 }
312
313 #define check_gerror(ok, error, fn) check_gerror_((ok)!=0, (error), (fn))
314
315 static void
316 check_gerror_matches_(
317     gboolean ok,
318     GError *error,
319     const char *matches,
320     const char *fn)
321 {
322     if (!ok && error) {
323         if (0 != strcmp(matches, error->message)) {
324             EXPECT_FAILURE(
325                     "%s produced error '%s' but expected '%s'\n",
326                     fn, error->message, matches);
327             exit(1);
328         }
329         return;
330     }
331
332     if (ok)
333         EXPECT_FAILURE(
334                 "'%s' correctly set 'error' but did not indicate an error condition: %s (%s)\n",
335                 fn, error->message, strerror(error->code));
336     else /* (!error) */
337         EXPECT_FAILURE(
338                 "'%s' correctly indicated an error condition but did not set 'error'.\n", fn);
339
340     exit(1);
341 }
342
343 #define check_gerror_matches(ok, error, match, fn) \
344     check_gerror_matches_((ok)!=0, (error), (match), (fn))
345
346 static void
347 try_reading_fd(
348         expected_step_t *steps,
349         amar_attr_handling_t *handling,
350         int fd)
351 {
352     amar_t *ar;
353     expected_state_t state = { steps, 0 };
354     GError *error = NULL;
355     gboolean ok;
356
357     ar = amar_new(fd, O_RDONLY, &error);
358     check_gerror(ar, error, "amar_new");
359     ok = amar_read(ar, &state, handling, file_start_cb, file_finish_cb, &error);
360     if (ok || error)
361         check_gerror(ok, error, "amar_read");
362     if (steps[state.curstep].kind != EXP_END)
363         EXPECT_FAILURE("Stopped reading early at step %d", state.curstep);
364     ok = amar_close(ar, &error);
365     check_gerror(ok, error, "amar_close");
366 }
367
368 static void
369 try_reading(
370         expected_step_t *steps,
371         amar_attr_handling_t *handling)
372 {
373     int fd;
374
375     fd = open_temp(0);
376     try_reading_fd(steps, handling, fd);
377     close(fd);
378 }
379
380 static void
381 try_reading_with_error(
382         expected_step_t *steps,
383         amar_attr_handling_t *handling,
384         const char *message)
385 {
386     amar_t *ar;
387     expected_state_t state = { steps, 0 };
388     int fd;
389     GError *error = NULL;
390     gboolean ok;
391
392     fd = open_temp(0);
393     ar = amar_new(fd, O_RDONLY, &error);
394     check_gerror(ar, error, "amar_new");
395     ok = amar_read(ar, &state, handling, file_start_cb, file_finish_cb, &error);
396     check_gerror_matches(ok, error, message, "amar_read");
397     amar_close(ar, NULL);
398     close(fd);
399 }
400
401 /****
402  * Test various valid inputs
403  */
404
405 static int
406 test_simple_read(void)
407 {
408     int fd;
409
410     fd = open_temp(1);
411     WRITE_HEADER(fd, 1);
412     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_FILENAME, 1, "/first/filename");
413     WRITE_RECORD_STR(fd, 1, 18, 1, "eighteen");
414     WRITE_HEADER(fd, 1);
415     WRITE_RECORD_STR(fd, 1, 19, 0, "nine");
416     WRITE_RECORD_STR(fd, 1, 20, 0, "twen");
417     WRITE_RECORD_STR(fd, 1, 19, 1, "teen");
418     WRITE_RECORD_STR(fd, 1, 20, 1, "ty");
419     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_EOF, 1, "");
420     close(fd);
421
422     {
423         amar_attr_handling_t handling[] = {
424             { 19, 256, frag_cb, NULL }, /* reassemble this attribute */
425             { 20, 0, frag_cb, NULL }, /* but pass along each fragment of this */
426             { 0, 256, frag_cb, NULL },
427         };
428         expected_step_t steps[] = {
429             EXPECT_START_FILE_STR(1, "/first/filename", 0),
430             EXPECT_ATTR_DATA_STR(1, 18, "eighteen", 1, 0),
431             EXPECT_ATTR_DATA_STR(1, 20, "twen", 0, 0),
432             EXPECT_ATTR_DATA_STR(1, 19, "nineteen", 1, 0),
433             EXPECT_ATTR_DATA_STR(1, 20, "ty", 1, 0),
434             EXPECT_FINISH_FILE(1, 0),
435             EXPECT_END(),
436         };
437         try_reading(steps, handling);
438     }
439
440     return 1;
441 }
442
443 static int
444 test_read_buffering(void)
445 {
446     int fd;
447
448     fd = open_temp(1);
449     WRITE_HEADER(fd, 1);
450     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_FILENAME, 1, "file1");
451     WRITE_HEADER(fd, 1);
452     WRITE_RECORD_STR(fd, 2, AMAR_ATTR_FILENAME, 1, "file2");
453     WRITE_RECORD_STR(fd, 2, 19, 0, "1"); /* one byte at a time, for 12 bytes */
454     WRITE_RECORD_STR(fd, 2, 19, 0, "9");
455     WRITE_RECORD_STR(fd, 2, 21, 1, "012345678901234567890123456789"); /* thirty bytes exactly */
456     WRITE_RECORD_STR(fd, 2, 19, 0, "1");
457     WRITE_RECORD_STR(fd, 1, 18, 0, "ATTR");
458     WRITE_RECORD_STR(fd, 2, 19, 0, "9");
459     WRITE_RECORD_STR(fd, 2, 19, 0, "1");
460     WRITE_RECORD_STR(fd, 2, 19, 0, "9");
461     WRITE_HEADER(fd, 1);
462     WRITE_RECORD_STR(fd, 1, 20, 0, "TWENTYTWE"); /* nine bytes, then three in the next frag */
463     WRITE_RECORD_STR(fd, 2, 19, 0, "1");
464     WRITE_RECORD_STR(fd, 1, 20, 1, "NTY");
465     WRITE_RECORD_STR(fd, 2, 19, 0, "9");
466     WRITE_RECORD_STR(fd, 1, 18, 0, "181818"); /* hit ten bytes exactly */
467     WRITE_RECORD_STR(fd, 2, 19, 0, "1");
468     WRITE_RECORD_STR(fd, 2, 19, 0, "9");
469     WRITE_RECORD_STR(fd, 1, 18, 0, "ATTR");
470     WRITE_RECORD_STR(fd, 1, 22, 0, "012345678"); /* nine bytes followed by 20 */
471     WRITE_RECORD_STR(fd, 1, 18, 1, "18");
472     WRITE_RECORD_STR(fd, 1, 22, 1, "01234567890123456789");
473     WRITE_RECORD_STR(fd, 2, 19, 0, "1");
474     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_EOF, 1, "");
475     WRITE_RECORD_STR(fd, 2, 19, 1, "9");
476     WRITE_RECORD_STR(fd, 2, AMAR_ATTR_EOF, 1, "");
477
478     close(fd);
479
480     {
481         amar_attr_handling_t handling[] = {
482             { 0, 10, frag_cb, NULL },   /* reassemble all fragments in 10-byte chunks */
483         };
484         expected_step_t steps[] = {
485             EXPECT_START_FILE_STR(1, "file1", 0),
486             EXPECT_START_FILE_STR(2, "file2", 0),
487             EXPECT_ATTR_DATA_STR(2, 21, "012345678901234567890123456789", 1, 0),
488             EXPECT_ATTR_DATA_STR(1, 20, "TWENTYTWENTY", 1, 0),
489             EXPECT_ATTR_DATA_STR(1, 18, "ATTR181818", 0, 0),
490             EXPECT_ATTR_DATA_STR(2, 19, "1919191919", 0, 0),
491             EXPECT_ATTR_DATA_STR(1, 18, "ATTR18", 1, 0),
492             EXPECT_ATTR_DATA_STR(1, 22, "01234567801234567890123456789", 1, 0),
493             EXPECT_FINISH_FILE(1, 0),
494             EXPECT_ATTR_DATA_STR(2, 19, "19", 1, 0),
495             EXPECT_FINISH_FILE(2, 0),
496             EXPECT_END(),
497         };
498         try_reading(steps, handling);
499     }
500
501     return 1;
502 }
503
504 static int
505 test_missing_eoa(void)
506 {
507     int fd;
508
509     fd = open_temp(1);
510     WRITE_HEADER(fd, 1);
511     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_FILENAME, 1, "file1");
512     WRITE_RECORD_STR(fd, 1, 21, 0, "attribu"); /* note no EOA */
513     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_EOF, 1, "");
514
515     close(fd);
516
517     {
518         amar_attr_handling_t handling[] = {
519             { 0, 1024, frag_cb, NULL },
520         };
521         expected_step_t steps[] = {
522             EXPECT_START_FILE_STR(1, "file1", 0),
523             EXPECT_ATTR_DATA_STR(1, 21, "attribu", 1, 1),
524             EXPECT_FINISH_FILE(1, 0),
525             EXPECT_END(),
526         };
527         try_reading(steps, handling);
528     }
529
530     return 1;
531 }
532
533 static int
534 test_ignore(void)
535 {
536     int fd;
537
538     fd = open_temp(1);
539     WRITE_HEADER(fd, 1);
540     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_FILENAME, 1, "file1");
541     WRITE_HEADER(fd, 1);
542     WRITE_RECORD_STR(fd, 2, AMAR_ATTR_FILENAME, 1, "file2");
543     WRITE_RECORD_STR(fd, 2, 20, 1, "attr20");
544     WRITE_RECORD_STR(fd, 1, 21, 0, "attr");
545     WRITE_RECORD_STR(fd, 1, 21, 1, "21");
546     WRITE_HEADER(fd, 1);
547     WRITE_RECORD_STR(fd, 3, AMAR_ATTR_FILENAME, 1, "file3");
548     WRITE_HEADER(fd, 1);
549     WRITE_RECORD_STR(fd, 4, AMAR_ATTR_FILENAME, 1, "file4");
550     WRITE_RECORD_STR(fd, 3, 22, 1, "attr22");
551     WRITE_RECORD_STR(fd, 4, 23, 1, "attr23");
552     WRITE_RECORD_STR(fd, 4, AMAR_ATTR_EOF, 1, "");
553     WRITE_RECORD_STR(fd, 3, AMAR_ATTR_EOF, 1, "");
554     WRITE_RECORD_STR(fd, 2, AMAR_ATTR_EOF, 1, "");
555     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_EOF, 1, "");
556
557     close(fd);
558
559     {
560         amar_attr_handling_t handling[] = {
561             { 0, 10, frag_cb, NULL },   /* reassemble all fragments in 10-byte chunks */
562         };
563         expected_step_t steps[] = {
564             EXPECT_START_FILE_STR(1, "file1", 1),
565             EXPECT_START_FILE_STR(2, "file2", 0),
566             EXPECT_ATTR_DATA_STR(2, 20, "attr20", 1, 0),
567             EXPECT_START_FILE_STR(3, "file3", 1),
568             EXPECT_START_FILE_STR(4, "file4", 0),
569             EXPECT_ATTR_DATA_STR(4, 23, "attr23", 1, 0),
570             EXPECT_FINISH_FILE(4, 0),
571             EXPECT_FINISH_FILE(2, 0),
572             EXPECT_END(),
573         };
574         try_reading(steps, handling);
575     }
576
577     return 1;
578 }
579
580 static int
581 test_missing_eof(void)
582 {
583     int fd;
584
585     fd = open_temp(1);
586     WRITE_HEADER(fd, 1);
587     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_FILENAME, 1, "file!");
588     WRITE_RECORD_STR(fd, 1, 20, 1, "attribute");
589     WRITE_RECORD_STR(fd, 1, 21, 0, "attribu"); /* note no EOA */
590
591     close(fd);
592
593     {
594         amar_attr_handling_t handling[] = {
595             { 0, 1024, frag_cb, NULL },
596         };
597         expected_step_t steps[] = {
598             EXPECT_START_FILE_STR(1, "file!", 0),
599             EXPECT_ATTR_DATA_STR(1, 20, "attribute", 1, 0),
600             EXPECT_ATTR_DATA_STR(1, 21, "attribu", 1, 1),
601             EXPECT_FINISH_FILE(1, 0),
602             EXPECT_END(),
603         };
604         try_reading(steps, handling);
605     }
606
607     return 1;
608 }
609
610 static int
611 test_extra_records(void)
612 {
613     int fd;
614
615     fd = open_temp(1);
616     WRITE_HEADER(fd, 1);
617     WRITE_RECORD_STR(fd, 4, AMAR_ATTR_EOF, 1, "");
618     WRITE_RECORD_STR(fd, 5, 20, 1, "old attribute");
619     WRITE_RECORD_STR(fd, 6, AMAR_ATTR_FILENAME, 1, "file!");
620     WRITE_RECORD_STR(fd, 6, 21, 0, "attribu"); /* note no EOA */
621     WRITE_RECORD_STR(fd, 5, AMAR_ATTR_EOF, 1, "");
622     WRITE_RECORD_STR(fd, 6, 21, 1, "te");
623     WRITE_RECORD_STR(fd, 6, AMAR_ATTR_EOF, 1, "");
624     close(fd);
625
626     {
627         amar_attr_handling_t handling[] = {
628             { 0, 1024, frag_cb, NULL },
629         };
630         expected_step_t steps[] = {
631             EXPECT_START_FILE_STR(6, "file!", 0),
632             EXPECT_ATTR_DATA_STR(6, 21, "attribute", 1, 0),
633             EXPECT_FINISH_FILE(6, 0),
634             EXPECT_END(),
635         };
636         try_reading(steps, handling);
637     }
638
639     return 1;
640 }
641
642 static gboolean
643 early_exit_frag_cb(
644         gpointer user_data G_GNUC_UNUSED,
645         uint16_t filenum G_GNUC_UNUSED,
646         gpointer file_data G_GNUC_UNUSED,
647         uint16_t attrid G_GNUC_UNUSED,
648         gpointer attrid_data G_GNUC_UNUSED,
649         gpointer *attr_data G_GNUC_UNUSED,
650         gpointer data G_GNUC_UNUSED,
651         gsize datasize G_GNUC_UNUSED,
652         gboolean eoa G_GNUC_UNUSED,
653         gboolean truncated G_GNUC_UNUSED)
654 {
655     return FALSE;
656 }
657
658 static int
659 test_early_exit(void)
660 {
661     int fd;
662
663     fd = open_temp(1);
664     WRITE_HEADER(fd, 1);
665     WRITE_RECORD_STR(fd, 6, AMAR_ATTR_FILENAME, 1, "file!");
666     WRITE_RECORD_STR(fd, 6, 21, 1, "attribu");
667     WRITE_RECORD_STR(fd, 6, AMAR_ATTR_EOF, 1, "");
668     close(fd);
669
670     {
671         amar_attr_handling_t handling[] = {
672             { 0, 0, early_exit_frag_cb, NULL },
673         };
674         expected_step_t steps[] = {
675             EXPECT_START_FILE_STR(6, "file!", 0),
676             EXPECT_FINISH_FILE(6, 1),
677             EXPECT_END(),
678         };
679         try_reading(steps, handling);
680     }
681
682     return 1;
683 }
684
685 /****
686  * Test the write side, using round trips.
687  */
688
689 /* just try to execute most of the writing code */
690 static int
691 test_writing_coverage(void)
692 {
693     int fd, fd2;
694     off_t posn, fdsize;
695     char buf[16300];
696     char buf2[16300];
697     char *bigbuf;
698     size_t bigbuf_size = 1024*50+93;
699     simpleprng_state_t prng;
700     gsize i;
701     guint16 attrid = 20;
702     amar_t *arch = NULL;
703     amar_file_t *af = NULL, *af2 = NULL;
704     amar_attr_t *at = NULL, *at2 = NULL;
705     GError *error = NULL;
706     gboolean ok;
707
708     /* set up some data buffers */
709     for (i = 0; i < sizeof(buf); i++) {
710         buf[i] = 0xfe;
711         buf2[i] = 0xaa;
712     }
713
714     bigbuf = g_malloc(bigbuf_size);
715     simpleprng_seed(&prng, 0xfeaa);
716     simpleprng_fill_buffer(&prng, bigbuf, bigbuf_size);
717
718     fd = open("amar-test.big", O_CREAT|O_WRONLY|O_TRUNC, 0777);
719     g_assert(fd >= 0);
720     g_assert(full_write(fd, bigbuf, bigbuf_size) == bigbuf_size);
721     close(fd);
722
723     fd = open_temp(1);
724
725     arch = amar_new(fd, O_WRONLY, &error);
726     check_gerror(arch, error, "amar_new");
727     g_assert(arch != NULL);
728
729     af = amar_new_file(arch, "MyFile", 0, &posn, &error);
730     check_gerror(af, error, "amar_new_file");
731     tu_dbg("MyFile starts at 0x%x\n", (int)posn)
732     g_assert(af != NULL);
733
734     /* by character with EOA */
735     at = amar_new_attr(af, attrid++, &error);
736     check_gerror(at, error, "amar_new_attr");
737     g_assert(at != NULL);
738     ok = amar_attr_add_data_buffer(at, buf, sizeof(buf), 1, &error);
739     check_gerror(ok, error, "amar_attr_add_data_buffer");
740     ok = amar_attr_close(at, &error);
741     check_gerror(ok, error, "amar_attr_close");
742
743     /* by character without EOA */
744     at = amar_new_attr(af, attrid++, &error);
745     check_gerror(at, error, "amar_new_attr");
746     g_assert(at != NULL);
747     ok = amar_attr_add_data_buffer(at, buf2, sizeof(buf2), 0, &error);
748     check_gerror(ok, error, "amar_attr_add_data_buffer");
749     ok = amar_attr_close(at, &error);
750     check_gerror(ok, error, "amar_attr_close");
751
752     /* open up a new file, for fun */
753     af2 = amar_new_file(arch, "MyOtherFile", 0, &posn, &error);
754     check_gerror(af2, error, "amar_new_file");
755     tu_dbg("MyOtherFile starts at 0x%x\n", (int)posn)
756
757     /* by file descriptor, to the first file */
758     at = amar_new_attr(af, attrid++, &error);
759     check_gerror(at, error, "amar_new_attr");
760     fd2 = open("amar-test.big", O_RDONLY);
761     g_assert(fd2 >= 0);
762     fdsize = amar_attr_add_data_fd(at, fd2, 0, &error);
763     check_gerror(fdsize != -1, error, "amar_attr_add_data_fd");
764     g_assert(fdsize > 0);
765     close(fd2);
766     unlink("amar-test.big");
767     ok = amar_attr_close(at, &error);
768     check_gerror(ok, error, "amar_attr_close");
769
770     ok = amar_file_close(af, &error);
771     check_gerror(ok, error, "amar_file_close");
772
773     /* interlaeave two attributes */
774     at = amar_new_attr(af2, attrid++, &error);
775     check_gerror(at, error, "amar_new_attr");
776     at2 = amar_new_attr(af2, attrid++, &error);
777     check_gerror(at2, error, "amar_new_attr");
778     ok = amar_attr_add_data_buffer(at, buf, 72, 0, &error);
779     check_gerror(ok, error, "amar_attr_add_data_buffer");
780     ok = amar_attr_add_data_buffer(at2, buf2, 72, 0, &error);
781     check_gerror(ok, error, "amar_attr_add_data_buffer");
782     ok = amar_attr_add_data_buffer(at, buf, 13, 0, &error);
783     check_gerror(ok, error, "amar_attr_add_data_buffer");
784     ok = amar_attr_add_data_buffer(at2, buf2, 13, 1, &error);
785     check_gerror(ok, error, "amar_attr_add_data_buffer");
786     ok = amar_attr_close(at, &error);
787     check_gerror(ok, error, "amar_attr_close");
788     ok = amar_attr_close(at2, &error);
789     check_gerror(ok, error, "amar_attr_close");
790
791     ok = amar_file_close(af2, &error);
792     check_gerror(ok, error, "amar_file_close");
793
794     ok = amar_close(arch, &error);
795     check_gerror(ok, error, "amar_close");
796     close(fd);
797
798     {
799         amar_attr_handling_t handling[] = {
800             { 22, bigbuf_size+1, frag_cb, NULL }, /* buffer the big attr */
801             { 0, 0, frag_cb, NULL },    /* don't buffer other records */
802         };
803         expected_step_t steps[] = {
804             EXPECT_START_FILE_STR(1, "MyFile", 0),
805             EXPECT_ATTR_DATA_MULTIPART(1, 20, buf, sizeof(buf), 1, 0),
806             EXPECT_ATTR_DATA_MULTIPART(1, 21, buf2, sizeof(buf2), 0, 0),
807             EXPECT_ATTR_DATA_MULTIPART(1, 21, buf2, 0, 1, 0), /* trailing EOA */
808             EXPECT_START_FILE_STR(2, "MyOtherFile", 0),
809             EXPECT_ATTR_DATA(1, 22, bigbuf, bigbuf_size, 1, 0),
810             EXPECT_FINISH_FILE(1, 0),
811             EXPECT_ATTR_DATA_MULTIPART(2, 23, buf, 72, 0, 0),
812             EXPECT_ATTR_DATA_MULTIPART(2, 24, buf2, 72, 0, 0),
813             EXPECT_ATTR_DATA_MULTIPART(2, 23, buf+72, 13, 0, 0),
814             EXPECT_ATTR_DATA_MULTIPART(2, 24, buf2+72, 13, 1, 0),
815             EXPECT_ATTR_DATA_MULTIPART(2, 23, buf, 0, 1, 0),
816             EXPECT_FINISH_FILE(2, 0),
817             EXPECT_END(),
818         };
819         try_reading(steps, handling);
820     }
821
822     return 1;
823 }
824
825 /* test big attributes */
826 static int
827 test_big_attr(void)
828 {
829     int fd, fd2;
830     off_t fdsize;
831     char *bigbuf;
832     const size_t max_record_data_size = 4*1024*1024;
833     size_t bigbuf_size = max_record_data_size + 1274; /* a record and a bit */
834     simpleprng_state_t prng;
835     guint16 attrid = 20;
836     amar_t *arch = NULL;
837     amar_file_t *af = NULL;
838     amar_attr_t *at = NULL;
839     GError *error = NULL;
840     gboolean ok;
841
842     /* set up some data buffers */
843     bigbuf = g_malloc(bigbuf_size);
844     simpleprng_seed(&prng, 0xb001);
845     simpleprng_fill_buffer(&prng, bigbuf, bigbuf_size);
846
847     fd = open("amar-test.big", O_CREAT|O_WRONLY|O_TRUNC, 0777);
848     g_assert(fd >= 0);
849     g_assert(full_write(fd, bigbuf, bigbuf_size) == bigbuf_size);
850     close(fd);
851
852     fd = open_temp(1);
853
854     arch = amar_new(fd, O_WRONLY, &error);
855     check_gerror(arch, error, "amar_new");
856
857     af = amar_new_file(arch, "bigstuff", 0, NULL, &error);
858     check_gerror(af, error, "amar_new_file");
859
860     /* by character */
861     at = amar_new_attr(af, attrid++, &error);
862     check_gerror(at, error, "amar_new_attr");
863     ok = amar_attr_add_data_buffer(at, bigbuf, bigbuf_size, 1, &error);
864     check_gerror(ok, error, "amar_attr_add_data_buffer");
865     ok = amar_attr_close(at, &error);
866     check_gerror(ok, error, "amar_attr_close");
867
868     /* by file descriptor */
869     at = amar_new_attr(af, attrid++, &error);
870     check_gerror(at, error, "amar_new_attr");
871     fd2 = open("amar-test.big", O_RDONLY);
872     g_assert(fd2 >= 0);
873     fdsize = amar_attr_add_data_fd(at, fd2, 1, &error);
874     check_gerror(fdsize != -1, error, "amar_attr_add_data_fd");
875     g_assert(fdsize > 0);
876     close(fd2);
877     unlink("amar-test.big");
878     ok = amar_attr_close(at, &error);
879     check_gerror(ok, error, "amar_attr_close");
880
881     ok = amar_file_close(af, &error);
882     check_gerror(ok, error, "amar_file_close");
883
884     ok = amar_close(arch, &error);
885     check_gerror(ok, error, "amar_close");
886     close(fd);
887
888     {
889         amar_attr_handling_t handling[] = {
890             { 0, 0, frag_cb, NULL },    /* don't buffer records */
891         };
892         expected_step_t steps[] = {
893             EXPECT_START_FILE_STR(1, "bigstuff", 0),
894             EXPECT_ATTR_DATA_MULTIPART(1, 20, bigbuf, max_record_data_size, 0, 0),
895             EXPECT_ATTR_DATA_MULTIPART(1, 20, bigbuf+max_record_data_size, bigbuf_size-max_record_data_size, 1, 0),
896             EXPECT_ATTR_DATA_MULTIPART(1, 21, bigbuf, max_record_data_size, 0, 0),
897             EXPECT_ATTR_DATA_MULTIPART(1, 21, bigbuf+max_record_data_size, bigbuf_size-max_record_data_size, 1, 0),
898             EXPECT_FINISH_FILE(1, 0),
899             EXPECT_END(),
900         };
901         try_reading(steps, handling);
902     }
903
904     return 1;
905 }
906
907 /* like test_big_attr, but using a pipe and ignoring one of the attrs in hopes
908  * of triggering an lseek(), which will fail on a pipe. */
909 static int
910 test_pipe(void)
911 {
912     int fd;
913     int p[2];
914     off_t fdsize;
915     char *bigbuf;
916     const size_t max_record_data_size = 4*1024*1024;
917     size_t bigbuf_size = max_record_data_size + 1274; /* a record and a bit */
918     simpleprng_state_t prng;
919     guint16 attrid = 20;
920     amar_t *arch = NULL;
921     amar_file_t *af = NULL;
922     amar_attr_t *at = NULL;
923     GError *error = NULL;
924     gboolean ok;
925
926     /* set up some data buffers */
927     bigbuf = g_malloc(bigbuf_size);
928     simpleprng_seed(&prng, 0xb001);
929     simpleprng_fill_buffer(&prng, bigbuf, bigbuf_size);
930
931     fd = open("amar-test.big", O_CREAT|O_WRONLY|O_TRUNC, 0777);
932     g_assert(fd >= 0);
933     g_assert(full_write(fd, bigbuf, bigbuf_size) == bigbuf_size);
934     close(fd);
935
936     g_assert(pipe(p) >= 0);
937
938     switch (fork()) {
939         case 0: /* child */
940             close(p[0]);
941             arch = amar_new(p[1], O_WRONLY, &error);
942             check_gerror(arch, error, "amar_new");
943             g_assert(arch != NULL);
944
945             af = amar_new_file(arch, "bigstuff", 0, NULL, &error);
946             check_gerror(af, error, "amar_new_file");
947
948             /* by character */
949             at = amar_new_attr(af, attrid++, &error);
950             check_gerror(at, error, "amar_new_attr");
951             ok = amar_attr_add_data_buffer(at, bigbuf, bigbuf_size, 1, &error);
952             check_gerror(ok, error, "amar_attr_add_data_buffer");
953             ok = amar_attr_close(at, &error);
954             check_gerror(ok, error, "amar_attr_close");
955
956             /* by file descriptor */
957             at = amar_new_attr(af, attrid++, &error);
958             check_gerror(at, error, "amar_new_attr");
959             fd = open("amar-test.big", O_RDONLY);
960             g_assert(fd >= 0);
961             fdsize = amar_attr_add_data_fd(at, fd, 1, &error);
962             check_gerror(fdsize != -1, error, "amar_attr_add_data_fd");
963             g_assert(fdsize > 0);
964             close(fd);
965             unlink("amar-test.big");
966             ok = amar_attr_close(at, &error);
967             check_gerror(ok, error, "amar_attr_close");
968
969             ok = amar_file_close(af, &error);
970             check_gerror(ok, error, "amar_file_close");
971
972             ok = amar_close(arch, &error);
973             check_gerror(ok, error, "amar_close");
974             close(p[1]);
975             exit(0);
976
977         case -1:
978             perror("fork");
979             exit(1);
980
981         default: { /* parent */
982             amar_attr_handling_t handling[] = {
983                 { 20, 0, NULL, NULL },          /* ignore attr 20 */
984                 { 0, 0, frag_cb, NULL },        /* don't buffer records */
985             };
986             expected_step_t steps[] = {
987                 EXPECT_START_FILE_STR(1, "bigstuff", 0),
988                 EXPECT_ATTR_DATA_MULTIPART(1, 21, bigbuf, max_record_data_size, 0, 0),
989                 EXPECT_ATTR_DATA_MULTIPART(1, 21, bigbuf+max_record_data_size, bigbuf_size-max_record_data_size, 1, 0),
990                 EXPECT_FINISH_FILE(1, 0),
991                 EXPECT_END(),
992             };
993             int status;
994             close(p[1]);
995             try_reading_fd(steps, handling, p[0]);
996             close(p[0]);
997             wait(&status);
998             if(WIFSIGNALED(status)) {
999                 printf("child was terminated by signal %d\n", WTERMSIG(status));
1000                 exit(1);
1001             }
1002         }
1003     }
1004
1005     return 1;
1006 }
1007
1008 /****
1009  * Invalid inputs - test error returns
1010  */
1011
1012 static int
1013 test_no_header(void)
1014 {
1015     int fd;
1016
1017     fd = open_temp(1);
1018     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_FILENAME, 1, "/first/filename");
1019     close(fd);
1020
1021     {
1022         amar_attr_handling_t handling[] = {
1023             { 0, 0, frag_cb, NULL },
1024         };
1025         expected_step_t steps[] = {
1026             EXPECT_END(),
1027         };
1028         try_reading_with_error(steps, handling,
1029                     "Archive read does not begin at a header record");
1030     }
1031
1032     return 1;
1033 }
1034
1035 static int
1036 test_invalid_eof(void)
1037 {
1038     int fd;
1039
1040     fd = open_temp(1);
1041     WRITE_HEADER(fd, 1);
1042     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_FILENAME, 1, "hi");
1043     WRITE_RECORD_STR(fd, 1, AMAR_ATTR_EOF, 1, "abc");
1044     close(fd);
1045
1046     {
1047         amar_attr_handling_t handling[] = {
1048             { 0, 0, frag_cb, NULL },
1049         };
1050         expected_step_t steps[] = {
1051             EXPECT_START_FILE_STR(1, "hi", 0),
1052             EXPECT_END(),
1053         };
1054         try_reading_with_error(steps, handling,
1055                     "Archive contains an EOF record with nonzero size");
1056     }
1057
1058     return 1;
1059 }
1060
1061 static int
1062 test_header_vers(void)
1063 {
1064     int fd;
1065     char hdr[32];
1066
1067     bzero(hdr, sizeof(hdr));
1068     strcpy(hdr, "AMANDA ARCHIVE FORMAT 2");
1069
1070     fd = open_temp(1);
1071     if (full_write(fd, hdr, sizeof(hdr)) < sizeof(hdr)) {
1072         perror("full_write");
1073         exit(1);
1074     }
1075     close(fd);
1076
1077     {
1078         amar_attr_handling_t handling[] = {
1079             { 0, 0, frag_cb, NULL },
1080         };
1081         expected_step_t steps[] = {
1082             EXPECT_END(),
1083         };
1084         try_reading_with_error(steps, handling,
1085                     "Archive version 2 is not supported");
1086     }
1087
1088     return 1;
1089 }
1090
1091 /****
1092  * Driver
1093  */
1094
1095 int
1096 main(int argc, char **argv)
1097 {
1098     int rv;
1099     char *cwd = g_get_current_dir();
1100     static TestUtilsTest tests[] = {
1101         TU_TEST(test_simple_read, 90),
1102         TU_TEST(test_read_buffering, 90),
1103         TU_TEST(test_missing_eoa, 90),
1104         TU_TEST(test_ignore, 90),
1105         TU_TEST(test_missing_eof, 90),
1106         TU_TEST(test_extra_records, 90),
1107         TU_TEST(test_early_exit, 90),
1108         TU_TEST(test_writing_coverage, 90),
1109         TU_TEST(test_big_attr, 90),
1110         TU_TEST(test_pipe, 90),
1111         TU_TEST(test_no_header, 90),
1112         TU_TEST(test_invalid_eof, 90),
1113         TU_TEST(test_header_vers, 90),
1114         TU_END()
1115     };
1116
1117     temp_filename = vstralloc(cwd, "/amar-test.tmp", NULL);
1118
1119     rv = testutils_run_tests(argc, argv, tests);
1120     unlink(temp_filename);
1121     return rv;
1122 }