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