PageRenderTime 7ms CodeModel.GetById 12ms app.highlight 64ms RepoModel.GetById 1ms app.codeStats 0ms

/amar-src/amar-test.c

https://bitbucket.org/bblough/amanda
C | 1122 lines | 897 code | 160 blank | 65 comment | 72 complexity | 46f9b0299c4ac63363aea0ee598b80b1 MD5 | raw file
   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
  33static 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
  64typedef enum {
  65    EXP_END,
  66    EXP_START_FILE,
  67    EXP_ATTRDATA,
  68    EXP_FINISH_FILE,
  69} expected_kind_t;
  70
  71typedef 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
  84typedef 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
 115static gboolean
 116file_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
 159static gboolean
 160file_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
 189static gboolean
 190frag_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
 278static int
 279open_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
 290static void
 291check_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
 315static void
 316check_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
 346static void
 347try_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
 368static void
 369try_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
 380static void
 381try_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
 405static int
 406test_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
 443static int
 444test_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
 504static int
 505test_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
 533static int
 534test_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
 580static int
 581test_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
 610static int
 611test_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
 642static gboolean
 643early_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
 658static int
 659test_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 */
 690static int
 691test_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 */
 826static int
 827test_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. */
 909static int
 910test_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
1012static int
1013test_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
1035static int
1036test_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
1061static int
1062test_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
1095int
1096main(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}