/* libmondo-stream.c $Id: libmondo-stream.c 1316 2007-04-16 14:50:28Z bruno $ ...tools for talking to tapes, Monitas streams, etc. 07/10 - added ACL and XATTR support 04/17 - added bkpinfo->internal_tape_block_size - fixed bug in set_tape_block_size_with_mt() 03/25 - call 'mt' when opening tape in/out, to set block size to 32K 02/06/2004 - only eject_device() at end of closein_tape() --- not closeout_tape() 10/26/2003 - call eject_device() at end of closein_tape() and closeout_tape() 09/25 - added docu-header to each subroutine 09/20 - working on multi-tape code - friendlier message in insist_on_this_tape_number() - delay is now 5s 09/10 - adding multi-tape support - adding 'back catalog' of previous archives, so that we may resume from point of end-of-tape (approx.) 09/08 - cleaned up insist_on_this_tape_number() --- 10s delay for same tape now 08/27 - improved find_tape_device_and_size() --- tries /dev/osst* 05/05 - added Joshua Oreman's FreeBSD patches 04/24 - added lots of assert()'s and log_OS_error()'s 04/06/2003 - use find_home_of_exe() instead of which 12/13/2002 - cdrecord -scanbus call was missing 2> /dev/null 11/24 - disabled fatal_error("Bad marker") 10/29 - replaced convoluted grep wih wc (KP) 09/07 - removed g_end_of_tape_reached - lots of multitape-related fixes 08/01 - 08/31 - write 16MB of zeroes to end of tape when closing(out) - compensate for 'buffer' and its distortion of speed of writing/reading when writing/reading data disks at start of tape - rewrote lots of multitape stuff - wrote workaround to allow >2GB of archives w/buffering - do not close/reopen tape when starting to read/write from new tape: no need! 'buffer' handles all that; we're writing to/reading from a FIFO, so no need to close/reopen when new tape - write 8MB of zeroes at end of tape, just in case - re-enable various calls to *_evalcall_form - added g_end_of_tape_reached - fixed bugs in start_to_[read|write]_[to|from]_next_tape - added internal buffering, replacing the external 'buffer' exe - wait 10 seconds (after user inserts new tape), to let tape stabilize in drive - added insist_on_this_tape_number() - worked on find_tape_device_and_size() - cleaned up some log_it() calls - added find_tape_device_and_size() - replaced using_cdstream with backup_media_type - replace *_from_tape with *_from_stream - replace *_to_stream with *_to_stream 07/01 - 07/31 - leave 32MB at end of tape, to avoid overrunning - started [07/24/2002] */ /** * @file * Functions for writing data to/reading data from streams (tape, CD stream, etc.) */ #include "my-stuff.h" #include "mondostructures.h" #include "libmondo-devices.h" #include "lib-common-externs.h" #include "libmondo-stream.h" #include "libmondo-string-EXT.h" #include "libmondo-files-EXT.h" #include "libmondo-gui-EXT.h" #include "libmondo-fork-EXT.h" #include "libmondo-tools-EXT.h" #include "libmondo-fifo-EXT.h" #define EXTRA_TAPE_CHECKSUMS /*@unused@*/ //static char cvsid[] = "$Id: libmondo-stream.c 1316 2007-04-16 14:50:28Z bruno $"; extern bool g_sigpipe; extern int g_tape_buffer_size_MB; extern char *g_getfacl; extern char *g_getfattr; extern char *MONDO_LOGFILE; /** * @addtogroup globalGroup * @{ */ /** * The file pointer for the opened tape/CD stream. * Opened and closed by openin_tape(), openin_cdstream(), * openout_tape(), openout_cdstream(), closein_tape(), closein_cdstream(), * closeout_tape(), and closeout_cdstream(). */ FILE *g_tape_stream = NULL; /** * The position (in kilobytes) where we are on the tape. */ long long g_tape_posK = 0; /** * The current media number we're using. This value is 1-based. */ int g_current_media_number = -1; /** * The tape catalog that keeps track of files written to tape. */ struct s_tapecatalog *g_tapecatalog; /* @} - end of globalGroup */ int write_backcatalog_to_tape(struct s_bkpinfo *bkpinfo); /** * @addtogroup streamGroup * @{ */ /** * Close the global output file descriptor which Mondo has used to read * from the CD stream. * @param bkpinfo The backup information structure. Passed directly to closein_tape(). * @return 0 for success, nonzero for failure. * @note This should be called by restore processes only. */ int closein_cdstream(struct s_bkpinfo *bkpinfo) { return (closein_tape(bkpinfo)); } /** * Close the global output file descriptor which Mondo has used to read * from buffer or dd, which read from the tape. * @param bkpinfo The backup information structure. Unused. * @return 0 for success, nonzero for failure. * @note This should be called by restore processes only. * @note This function also works for cdstreams for now, but don't count on this behavior. * @bug @p bkpinfo parameter is unused. */ int closein_tape(struct s_bkpinfo *bkpinfo) { /*@ int's ******************************************************* */ int retval = 0; int res = 0; int ctrl_chr = '\0'; /*@ buffers ***************************************************** */ char fname[MAX_STR_LEN]; /*@ long long's ************************************************* */ long long size; char *blk; int i; blk = (char *) malloc(256 * 1024); log_it("closein_tape() -- entering"); res = read_header_block_from_stream(&size, fname, &ctrl_chr); retval += res; if (ctrl_chr != BLK_END_OF_BACKUP) { wrong_marker(BLK_END_OF_BACKUP, ctrl_chr); } res = read_header_block_from_stream(&size, fname, &ctrl_chr); retval += res; if (ctrl_chr != BLK_END_OF_TAPE) { wrong_marker(BLK_END_OF_TAPE, ctrl_chr); } for (i = 0; i < 8 && !feof(g_tape_stream); i++) { (void) fread(blk, 1, 256 * 1024, g_tape_stream); } sleep(1); paranoid_system("sync"); sleep(1); paranoid_pclose(g_tape_stream); log_it("closein_tape() -- leaving"); /* for(i=0; i < g_tapecatalog->entries; i++) { log_it("i=%d type=%s num=%d aux=%ld", i, (g_tapecatalog->el[i].type==fileset)?"fileset":"bigslice", g_tapecatalog->el[i].number, g_tapecatalog->el[i].aux); } */ if (!bkpinfo->please_dont_eject) { eject_device(bkpinfo->media_device); } paranoid_free(blk); paranoid_free(g_tapecatalog); return (retval); } /** * Close the global output file descriptor which Mondo has been using to write * to the tape device (via buffer or dd). * @param bkpinfo The backup information structure. @c bkpinfo->media_size is the only field used. * @return 0 for success, nonzero for failure. * @note This should be called by backup processes only. */ int closeout_tape(struct s_bkpinfo *bkpinfo) { /*@ int's ******************************************************* */ int retval = 0; // int res = 0; // int ctrl_chr = '\0'; /*@ buffers ***************************************************** */ // char fname[MAX_STR_LEN]; /*@ long long's ************************************************* */ // long long size; int i; char *blk; blk = (char *) malloc(256 * 1024); sleep(1); paranoid_system("sync"); sleep(1); log_it("closeout_tape() -- entering"); retval += write_header_block_to_stream((off_t)0, "end-of-backup", BLK_END_OF_BACKUP); retval += write_header_block_to_stream((off_t)0, "end-of-tape", BLK_END_OF_TAPE); /* just in case */ /* write 1MB of crap */ for (i = 0; i < 256 * 1024; i++) { blk[i] = (int) (random() & 0xFF); } for (i = 0; i < 4 * 8; i++) { (void) fwrite(blk, 1, 256 * 1024, g_tape_stream); if (should_we_write_to_next_tape (bkpinfo->media_size[g_current_media_number], (off_t)256 * 1024)) { start_to_write_to_next_tape(bkpinfo); } } /* write 1MB of zeroes */ /* for (i = 0; i < 256*1024; i++) { blk[i] = 0; } for (i = 0; i < 4; i++) { fwrite (blk, 1, 256*1024, g_tape_stream); if (should_we_write_to_next_tape (bkpinfo->media_size[g_current_media_number], 256*1024)) { start_to_write_to_next_tape (bkpinfo); } } */ sleep(2); paranoid_pclose(g_tape_stream); log_it("closeout_tape() -- leaving"); for (i = 0; i < g_tapecatalog->entries; i++) { log_it("i=%d type=%s num=%d aux=%ld posK=%lld", i, (g_tapecatalog->el[i].type == fileset) ? "fileset" : "bigslice", g_tapecatalog->el[i].number, g_tapecatalog->el[i].aux, g_tapecatalog->el[i].tape_posK); } // if (!bkpinfo->please_dont_eject) // { eject_device(bkpinfo->media_device); } paranoid_free(blk); paranoid_free(g_tapecatalog); return (retval); } bool mt_says_tape_exists(char *dev) { char *command; int res; malloc_string(command); sprintf(command, "mt -f %s status", dev); res = run_program_and_log_output(command, 1); paranoid_free(command); if (res) { return (FALSE); } else { return (TRUE); } } /** * Determine the name and size of the tape device. Tries the SCSI tape for * this platform, then the IDE tape, then "/dev/st0", then "/dev/osst0". * @param dev Where to put the found tape device. * @param siz Where to put the tape size (a string like "4GB") * @return 0 if success, nonzero if failure (in which @p dev and @p siz are undefined). */ int find_tape_device_and_size(char *dev, char *siz) { char tmp[MAX_STR_LEN]; char command[MAX_STR_LEN * 2]; char cdr_exe[MAX_STR_LEN]; // char tape_description_[MAX_STR_LEN]; // char tape_manufacturer_cdr[MAX_STR_LEN]; // FILE*fin; int res; log_to_screen("I am looking for your tape streamer. Please wait."); dev[0] = siz[0] = '\0'; if (find_home_of_exe("cdrecord")) { strcpy(cdr_exe, "cdrecord"); } else { strcpy(cdr_exe, "dvdrecord"); } sprintf(command, "%s -scanbus 2> /dev/null | grep -i tape | wc -l", cdr_exe); strcpy(tmp, call_program_and_get_last_line_of_output(command)); if (atoi(tmp) != 1) { log_it ("Either too few or too many tape streamers for me to detect..."); strcpy(dev, VANILLA_SCSI_TAPE); return 1; } sprintf(command, "%s -scanbus 2> /dev/null | tr -s '\t' ' ' | grep \"[0-9]*,[0-9]*,[0-9]*\" | grep -v \"[0-9]*) \\*\" | grep -i TAPE | cut -d' ' -f2 | head -n1", cdr_exe); strcpy(tmp, call_program_and_get_last_line_of_output(command)); if (strlen(tmp) < 2) { log_it("Could not find tape device"); return 1; } sprintf(command, "%s -scanbus 2> /dev/null | tr -s '\t' ' ' | grep \"[0-9]*,[0-9]*,[0-9]*\" | grep -v \"[0-9]*) \\*\" | grep -i TAPE | cut -d' ' -f3 | cut -d')' -f1 | head -n1", cdr_exe); strcpy(tmp, call_program_and_get_last_line_of_output(command)); strcpy(dev, VANILLA_SCSI_TAPE); dev[strlen(dev) - 1] = '\0'; strcat(dev, tmp); // e.g. '/dev/st0' becomes '/dev/stN' res = 0; if (!mt_says_tape_exists(dev)) { strcpy(dev, ALT_TAPE); if (!mt_says_tape_exists(dev)) { log_it("Cannot openin %s", dev); strcpy(dev, "/dev/st0"); if (!mt_says_tape_exists(dev)) { log_it("Cannot openin %s", dev); strcpy(dev, "/dev/osst0"); if (!mt_says_tape_exists(dev)) { res++; } else { res = 0; } } } } log_it("At this point, dev = %s and res = %d", dev, res); strcpy(tmp, call_program_and_get_last_line_of_output("\ cdrecord -scanbus 2> /dev/null | tr -s '\t' ' ' | \ grep \"[0-9]*,[0-9]*,[0-9]*\" | grep -v \"[0-9]*) \\*\" | grep -i TAPE | \ awk '{for(i=1; i0) { print $i;};};};'")); if (mt_says_tape_exists(dev)) { res = 0; } else { log_it("Turning %s", dev); strcpy(tmp, (strrchr(dev, '/') != NULL) ? strrchr(dev, '/') : dev); sprintf(dev, "/dev/os%s", tmp); log_it("...into %s", dev); if (mt_says_tape_exists(dev)) { res = 0; } else { res++; } } siz[0] = '\0'; log_it("res=%d; dev=%s", res, dev); if (res) { return (res); } if (strlen(tmp) < 2) { siz[0] = '\0'; log_it("Warning - size of tape unknown"); return (0); } else { strcpy(siz, tmp); return (0); } } int read_EXAT_files_from_tape(struct s_bkpinfo *bkpinfo, long long *ptmp_size, char *tmp_fname, int *pctrl_chr, char *xattr_fname, char *acl_fname) { int res = 0; int retval = 0; char *fname = (char *)&res; /* Should NOT be NULL */ // xattr if (g_getfattr) { res = read_header_block_from_stream(ptmp_size, fname, pctrl_chr); if (*pctrl_chr != BLK_START_EXAT_FILE) { wrong_marker(BLK_START_EXAT_FILE, *pctrl_chr); } if (!strstr(fname, "xattr")) { fatal_error("Wrong order, sunshine."); } read_file_from_stream_to_file(bkpinfo, xattr_fname, *ptmp_size); res = read_header_block_from_stream(ptmp_size, tmp_fname, pctrl_chr); if (*pctrl_chr != BLK_STOP_EXAT_FILE) { wrong_marker(BLK_STOP_EXAT_FILE, *pctrl_chr); } log_msg(1, "Got xattr"); } // acl if (g_getfacl) { res = read_header_block_from_stream(ptmp_size, fname, pctrl_chr); if (!strstr(fname, "acl")) { fatal_error("Wrong order, sunshine."); } if (*pctrl_chr != BLK_START_EXAT_FILE) { wrong_marker(BLK_START_EXAT_FILE, *pctrl_chr); } read_file_from_stream_to_file(bkpinfo, acl_fname, *ptmp_size); res = read_header_block_from_stream(ptmp_size, tmp_fname, pctrl_chr); if (*pctrl_chr != BLK_STOP_EXAT_FILE) { wrong_marker(BLK_STOP_EXAT_FILE, *pctrl_chr); } res = read_header_block_from_stream(ptmp_size, tmp_fname, pctrl_chr); if (*pctrl_chr != BLK_STOP_EXTENDED_ATTRIBUTES) { wrong_marker(BLK_STOP_EXTENDED_ATTRIBUTES, *pctrl_chr); } log_msg(1, "Got acl"); } // tarball itself res = read_header_block_from_stream(ptmp_size, tmp_fname, pctrl_chr); log_msg(1, "now looking for afioball"); return (retval); } int write_EXAT_files_to_tape(struct s_bkpinfo *bkpinfo, char *xattr_fname, char *acl_fname) { int res = 0; if (g_getfattr) { // xattr write_header_block_to_stream(length_of_file(xattr_fname), xattr_fname, BLK_START_EXTENDED_ATTRIBUTES); write_header_block_to_stream(length_of_file(xattr_fname), xattr_fname, BLK_START_EXAT_FILE); write_file_to_stream_from_file(bkpinfo, xattr_fname); write_header_block_to_stream((off_t)-1, xattr_fname, BLK_STOP_EXAT_FILE); } if (g_getfacl) { // acl write_header_block_to_stream(length_of_file(acl_fname), acl_fname, BLK_START_EXAT_FILE); write_file_to_stream_from_file(bkpinfo, acl_fname); write_header_block_to_stream((off_t)-1, acl_fname, BLK_STOP_EXAT_FILE); write_header_block_to_stream(length_of_file(xattr_fname), xattr_fname, BLK_STOP_EXTENDED_ATTRIBUTES); } return (res); } /** * Tell the user to insert tape @p tapeno and wait for it to settle (5 seconds). * @param tapeno The tape number to insist on. * @bug There is currently no way to actually verify that the user has actually * inserted the right tape. */ void insist_on_this_tape_number(int tapeno) { int i; char tmp[MAX_STR_LEN]; log_it("Insisting on tape #%d", tapeno); if (g_current_media_number != tapeno) { // log_it("g_current_media_number = %d", g_current_media_number); sprintf(tmp, "When the tape drive goes quiet, please insert volume %d in this series.", tapeno); popup_and_OK(tmp); open_evalcall_form("Waiting while the tape drive settles"); } else { open_evalcall_form("Waiting while the tape drive rewinds"); } for (i = 0; i <= 100; i += 2) { usleep(100000); update_evalcall_form(i); } close_evalcall_form(); log_it("I assume user has inserted it. They _say_ they have..."); g_current_media_number = tapeno; // log_it("g_current_media_number = %d", g_current_media_number); log_it("OK, I've finished insisting. On with the revelry."); } /** * Debugging aid - log the offset we're at on the tape (reading or writing). */ void log_tape_pos(void) { /*@ buffers ***************************************************** */ /*@ end vars *************************************************** */ log_it("Tape position -- %ld KB (%ld MB)", (long) g_tape_posK, (long) g_tape_posK >> 10); } /** * Add a file to a collection of recently archived filesets/slices. * The type is determined by the filename: if it contains ".afio." it is * assumed to be a fileset, otherwise if it contains "slice" it's a slice, * otherwise we generate a fatal_error(). * @param td The current @c bkpinfo->tempdir (file will be placed in td/tmpfs/backcatalog/). * @param latest_fname The file to place in the collection. * @return 0, always. * @bug Return value is redundant. * @bug The detection won't work for uncompressed afioballs (they end in ".afio", no dot afterwards). // Not true. They end in '.' -Hugo */ int maintain_collection_of_recent_archives(char *td, char *latest_fname) { long long final_alleged_writeK, final_projected_certain_writeK, final_actually_certain_writeK = 0, cposK, bufsize_K; int last, curr, i; t_archtype type = other; char command[MAX_STR_LEN]; char tmpdir[MAX_STR_LEN]; char old_fname[MAX_STR_LEN]; char *p; char suffix[16]; bufsize_K = (long long) (1024LL * (1 + g_tape_buffer_size_MB)); sprintf(tmpdir, "%s/tmpfs/backcatalog", td); if ((p = strrchr(latest_fname, '.'))) { strcpy(suffix, ++p); } else { suffix[0] = '\0'; } if (strstr(latest_fname, ".afio.") || strstr(latest_fname, ".star.")) { type = fileset; } else if (strstr(latest_fname, "slice")) { type = biggieslice; } else { log_it("fname = %s", latest_fname); fatal_error ("Unknown type. Internal error in maintain_collection_of_recent_archives()"); } mkdir(tmpdir, 0x700); sprintf(command, "cp -f %s %s", latest_fname, tmpdir); if (run_program_and_log_output(command, 6)) { log_it("Warning - failed to copy %s to backcatalog at %s", latest_fname, tmpdir); } last = g_tapecatalog->entries - 1; if (last <= 0) { iamhere("Too early to start deleting from collection."); return (0); } final_alleged_writeK = g_tapecatalog->el[last].tape_posK; final_projected_certain_writeK = final_alleged_writeK - bufsize_K; for (curr = last; curr >= 0; curr--) { cposK = g_tapecatalog->el[curr].tape_posK; if (cposK < final_projected_certain_writeK) { final_actually_certain_writeK = cposK; break; } } if (curr < 0) { iamhere ("Not far enough into tape to start deleting old archives from collection."); return (0); } // log_it( "There are %lld KB (more than %d KB) in my backcatalog", final_alleged_writeK - final_actually_certain_writeK, bufsize_K); for (i = curr - 1; i >= 0 && curr - i < 10; i--) { sprintf(old_fname, "%s/%s", tmpdir, g_tapecatalog->el[i].fname); unlink(old_fname); } return (0); } /** * Open the CD stream for input. * @param bkpinfo The backup information structure. Passed to openin_tape(). * @return 0 for success, nonzero for failure. * @note Equivalent to openin_tape() for now, but don't count on this behavior. */ int openin_cdstream(struct s_bkpinfo *bkpinfo) { return (openin_tape(bkpinfo)); } /** * FIFO used to read/write to the tape device. * @bug This seems obsolete now that we call an external @c buffer program. Please look onto this. */ char g_tape_fifo[MAX_STR_LEN]; int set_tape_block_size_with_mt(char *tapedev, long internal_tape_block_size) { char *tmp; int res; if (strncmp(tapedev, "/dev/", 5)) { log_msg(1, "Not using 'mt setblk'. This isn't an actual /dev entry."); return (0); } malloc_string(tmp); sprintf(tmp, "mt -f %s setblk %ld", tapedev, internal_tape_block_size); res = run_program_and_log_output(tmp, 3); paranoid_free(tmp); return (res); } /** * Open the tape device for input. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->media_device * - @c bkpinfo->tmpdir * @return 0 for success, nonzero for failure. * @note This will also work with a cdstream for now, but don't count on this behavior. */ int openin_tape(struct s_bkpinfo *bkpinfo) { /*@ buffer ***************************************************** */ char fname[MAX_STR_LEN]; char *datablock; char tmp[MAX_STR_LEN]; char old_cwd[MAX_STR_LEN]; char outfname[MAX_STR_LEN]; /*@ int ******************************************************* */ int i; int j; int res; long length, templong; size_t k; int retval = 0; int ctrl_chr; /*@ long long ************************************************* */ long long size; /*@ pointers ************************************************** */ FILE *fout; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(bkpinfo->media_device); if (!(g_tapecatalog = malloc(sizeof(struct s_tapecatalog)))) { fatal_error("Cannot alloc mem for tape catalog"); } g_tapecatalog->entries = 0; g_tape_posK = 0; if (g_tape_stream) { log_it("FYI - I won't 'openin' the tape. It's already open."); return (0); } insist_on_this_tape_number(1); sprintf(outfname, "%s/tmp/all.tar.gz", bkpinfo->tmpdir); make_hole_for_file(outfname); set_tape_block_size_with_mt(bkpinfo->media_device, bkpinfo->internal_tape_block_size); // start_buffer_process( bkpinfo->media_device, g_tape_fifo, FALSE); log_it("Opening IN tape"); if (! (g_tape_stream = open_device_via_buffer(bkpinfo->media_device, 'r', bkpinfo->internal_tape_block_size))) { log_OS_error(g_tape_fifo); log_to_screen("Cannot openin stream device"); return (1); } log_to_screen("Reading stream"); log_it("stream device = '%s'", bkpinfo->media_device); /* skip data disks */ open_evalcall_form("Skipping data disks on stream"); log_to_screen("Skipping data disks on stream"); if (!(fout = fopen(outfname, "w"))) { log_OS_error(outfname); log_to_screen("Cannot openout datadisk all.tar.gz file"); return (-1); } if (!(datablock = (char *) malloc(256 * 1024))) { log_to_screen("Unable to malloc 256*1024"); exit(1); } for (i = 0; i < 32; i++) { for (j = 0; j < 4; j++) { for (length = 0, k = 0; length < 256 * 1024; length += k) { k = fread(datablock + length, 1, 256 * 1024 - length, g_tape_stream); } (void) fwrite(datablock, 1, (size_t) length, fout); g_tape_posK += length / 1024; } if (i > 8) // otherwise, 'buffer' distorts calculations { templong = ((i - 8) * 4 + j) * 100 / (128 - 8 * 4); update_evalcall_form((int) (templong)); } } paranoid_fclose(fout); paranoid_free(datablock); /* find initial blocks */ res = read_header_block_from_stream(&size, fname, &ctrl_chr); retval += res; if (ctrl_chr != BLK_START_OF_TAPE) { wrong_marker(BLK_START_OF_TAPE, ctrl_chr); } res = read_header_block_from_stream(&size, fname, &ctrl_chr); retval += res; if (ctrl_chr != BLK_START_OF_BACKUP) { wrong_marker(BLK_START_OF_BACKUP, ctrl_chr); } close_evalcall_form(); log_it("Saved all.tar.gz to '%s'", outfname); (void) getcwd(old_cwd, MAX_STR_LEN); chdir(bkpinfo->tmpdir); sprintf(tmp, "tar -zxf %s tmp/mondo-restore.cfg 2> /dev/null", outfname); paranoid_system(tmp); paranoid_system("cp -f tmp/mondo-restore.cfg . 2> /dev/null"); chdir(old_cwd); unlink(outfname); return (retval); } /** * Start writing to a CD stream. * @param cddev The CD device to openout via cdrecord. * @param speed The speed to write at. * @return 0 for success, nonzero for failure. * @note This should be called only from backup processes. */ int openout_cdstream(char *cddev, int speed) { /*@ buffers ***************************************************** */ char command[MAX_STR_LEN * 2]; /*@ end vars *************************************************** */ /* add 'dummy' if testing */ sprintf(command, "cdrecord -eject dev=%s speed=%d fs=24m -waiti - >> %s 2>> %s", cddev, speed, MONDO_LOGFILE, MONDO_LOGFILE); /* initialise the catalog */ g_current_media_number = 1; if (!(g_tapecatalog = malloc(sizeof(struct s_tapecatalog)))) { fatal_error("Cannot alloc mem for tape catalog"); } g_tapecatalog->entries = 0; /* log stuff */ log_it("Opening OUT cdstream with the command"); log_it(command); /* log_it("Let's see what happens, shall we?"); */ g_tape_stream = popen(command, "w"); if (g_tape_stream) { return (0); } else { log_to_screen("Failed to openout to cdstream (fifo)"); return (1); } } /** * Start writing to a tape device for the backup. * @param tapedev The tape device to open for writing. * @return 0 for success, nonzero for failure. * @note This should be called ONLY from backup processes. It will OVERWRITE ANY * EXISTING DATA on the tape! */ int openout_tape(char *tapedev, long internal_tape_block_size) { // char sz_call_to_buffer[MAX_STR_LEN]; g_current_media_number = 1; if (g_tape_stream) { log_it("FYI - I won't 'openout' the tape. It's already open."); return (0); } if (!(g_tapecatalog = malloc(sizeof(struct s_tapecatalog)))) { fatal_error("Cannot alloc mem for tape catalog"); } g_tapecatalog->entries = 0; g_tape_posK = 0; set_tape_block_size_with_mt(tapedev, internal_tape_block_size); log_it("Opening OUT tape"); if (! (g_tape_stream = open_device_via_buffer(tapedev, 'w', internal_tape_block_size))) { log_OS_error(g_tape_fifo); log_to_screen("Cannot openin stream device"); return (1); } return (0); } /** * Copy a file from the opened stream (CD or tape) to @p outfile. * @param bkpinfo The backup information structure. @c bkpinfo->media_device is the only field used. * @param outfile The file to write to. * @param size The size of the file in the input stream. * @return 0 for success, nonzero for failure. */ int read_file_from_stream_to_file(struct s_bkpinfo *bkpinfo, char *outfile, long long size) { /*@ int ******************************************************** */ int res; /*@ end vars *************************************************** */ res = read_file_from_stream_FULL(bkpinfo, outfile, NULL, size); return (res); } /** * Copy a file from the currently opened stream (CD or tape) to the stream indicated * by @p fout. * @param bkpinfo The backup information structure. @c bkpinfo->media_size is the only field used. * @param fout The stream to write the file to. * @param size The size of the file in bytes. * @return 0 for success, nonzero for failure. */ int read_file_from_stream_to_stream(struct s_bkpinfo *bkpinfo, FILE * fout, long long size) { /*@ int ******************************************************** */ int res; /*@ end vars *************************************************** */ res = read_file_from_stream_FULL(bkpinfo, NULL, fout, size); /* fflush(g_tape_stream); fflush(fout);*/ return (res); } /** * Copy a file from the currently opened stream (CD or tape) to either a * @c FILE pointer indicating a currently-opened file, or a filename indicating * a new file to create. This is the backbone function that read_file_from_stream_to_file() * and read_file_from_stream_to_stream() are wrappers for. * @param bkpinfo The backup information structure. @c bkpinfo->media_size is the only field used. * @param outfname If non-NULL, write to this file. * @param foutstream If non-NULL, write to this stream. * @param orig_size The original length of the file in bytes. * @return 0 for success, nonzero for failure. * @note Only @b one of @p outfname or @p foutstream may be non-NULL. */ int read_file_from_stream_FULL(struct s_bkpinfo *bkpinfo, char *outfname, FILE * foutstream, long long orig_size) { /*@ buffers ***************************************************** */ char *tmp; char *datablock; char *temp_fname; char *temp_cksum; char *actual_cksum; // char *pA, *pB; /*@ int ********************************************************* */ int retval = 0; #ifdef EXTRA_TAPE_CHECKSUMS int i, ch; #endif int noof_blocks; int ctrl_chr; int res; /*@ pointers **************************************************** */ FILE *fout; /*@ long ***************************************************** */ long bytes_to_write = 0 /*,i */ ; // long bytes_successfully_read_in_this_time = 0; /*@ long long *************************************************** */ long long temp_size, size; long bytes_read, bytes_to_read; long long total_read_from_tape_for_this_file = 0; long long where_I_was_before_tape_change = 0; /*@ unsigned int ************************************************ */ /* unsigned int ch; */ unsigned int crc16; unsigned int crctt; bool had_to_resync = FALSE; /*@ init ******************************************************* */ malloc_string(tmp); malloc_string(temp_fname); malloc_string(temp_cksum); malloc_string(actual_cksum); datablock = malloc(TAPE_BLOCK_SIZE); crc16 = 0; crctt = 0; size = orig_size; /*@ end vars *************************************************** */ res = read_header_block_from_stream(&temp_size, temp_fname, &ctrl_chr); if (orig_size != temp_size && orig_size != -1) { sprintf(tmp, "output file's size should be %ld K but is apparently %ld K", (long) size >> 10, (long) temp_size >> 10); log_to_screen(tmp); } if (ctrl_chr != BLK_START_FILE) { wrong_marker(BLK_START_FILE, ctrl_chr); return (1); } sprintf(tmp, "Reading file from tape; writing to '%s'; %ld KB", outfname, (long) size >> 10); if (foutstream) { fout = foutstream; } else { fout = fopen(outfname, "w"); } if (!fout) { log_OS_error(outfname); log_to_screen("Cannot openout file"); return (1); } total_read_from_tape_for_this_file = 0; for (noof_blocks = 0; size > 0; noof_blocks++, size -= bytes_to_write, total_read_from_tape_for_this_file += bytes_read) { bytes_to_write = (size < TAPE_BLOCK_SIZE) ? (long) size : TAPE_BLOCK_SIZE; bytes_to_read = TAPE_BLOCK_SIZE; bytes_read = fread(datablock, 1, bytes_to_read, g_tape_stream); while (bytes_read < bytes_to_read) { // next tape! // crctt=crc16=0; where_I_was_before_tape_change = size; log_msg(4, "where_I_was_... = %lld", where_I_was_before_tape_change); start_to_read_from_next_tape(bkpinfo); log_msg(4, "Started reading from next tape."); skip_incoming_files_until_we_find_this_one(temp_fname); log_msg(4, "Skipped irrelevant files OK."); for (size = orig_size; size > where_I_was_before_tape_change; size -= bytes_to_write) { bytes_read = fread(datablock, 1, bytes_to_read, g_tape_stream); } log_msg(4, "'size' is now %lld (should be %lld)", size, where_I_was_before_tape_change); log_to_screen("Successfully re-sync'd tape"); had_to_resync = TRUE; bytes_read = fread(datablock, 1, bytes_to_read, g_tape_stream); } (void) fwrite(datablock, 1, (size_t) bytes_to_write, fout); // for blocking reasons, bytes_successfully_read_in isn't necessarily the same as bytes_to_write #ifdef EXTRA_TAPE_CHECKSUMS for (i = 0; i < (int) bytes_to_write; i++) { ch = datablock[i]; crc16 = updcrcr(crc16, (unsigned) ch); crctt = updcrc(crctt, (unsigned) ch); } #endif } log_msg(6, "Total read from tape for this file = %lld", total_read_from_tape_for_this_file); log_msg(6, ".......................... Should be %lld", orig_size); g_tape_posK += total_read_from_tape_for_this_file / 1024; sprintf(actual_cksum, "%04x%04x", crc16, crctt); if (foutstream) { /*log_it("Finished writing to foutstream"); */ } else { paranoid_fclose(fout); } res = read_header_block_from_stream(&temp_size, temp_cksum, &ctrl_chr); if (ctrl_chr != BLK_STOP_FILE) { wrong_marker(BLK_STOP_FILE, ctrl_chr); // fatal_error("Bad marker"); // return(1); } if (strcmp(temp_cksum, actual_cksum)) { sprintf(tmp, "actual cksum=%s; recorded cksum=%s", actual_cksum, temp_cksum); log_to_screen(tmp); sprintf(tmp, "%s (%ld K) is corrupt on tape", temp_fname, (long) orig_size >> 10); log_to_screen(tmp); retval++; } else { sprintf(tmp, "%s is GOOD on tape", temp_fname); /* log_it(tmp); */ } paranoid_free(datablock); paranoid_free(tmp); paranoid_free(temp_fname); paranoid_free(temp_cksum); paranoid_free(actual_cksum); return (retval); } /** * Read a header block from the currently opened stream (CD or tape). * This block indicates the length of the following file (if it's file-related) * the filename (if it's file-related), and the block type. * @param plen Where to put the length of the file. Valid only for file-related header blocks. * @param filename Where to put the name of the file. Valid only for file-related header blocks. * @param pcontrol_char Where to put the type of block (e.g. start-file, end-file, start-tape, ...) * @return 0 for success, nonzero for failure. * @note If you read a marker (@p pcontrol_char) you're not expecting, you can call wrong_marker(). */ int read_header_block_from_stream(long long *plen, char *filename, int *pcontrol_char) { /*@ buffers ***************************************************** */ char *tempblock; /*@ int ********************************************************* */ int i, retval; /*@ end vars *************************************************** */ tempblock = (char *) malloc((size_t) TAPE_BLOCK_SIZE); for (i = 0; i < (int) TAPE_BLOCK_SIZE; i++) { tempblock[i] = 0; } while (!(*pcontrol_char = tempblock[7000])) { g_tape_posK += fread(tempblock, 1, (size_t) TAPE_BLOCK_SIZE, g_tape_stream) / 1024; } /* memcpy((char*)plength_of_incoming_file,(char*)tempblock+7001,sizeof(long long)); */ /* for(*plen=0,i=7;i>=0;i--) {*plen<<=8; *plen |= tempblock[7001+i];} */ memcpy((char *) plen, tempblock + 7001, sizeof(long long)); if (strcmp(tempblock + 6000 + *pcontrol_char, "Mondolicious, baby")) { log_it("Bad header block at %ld K", (long) g_tape_posK); } strcpy(filename, tempblock + 1000); /* strcpy(cksum,tempblock+5555);*/ /* log_it( "%s (reading) fname=%s, filesize=%ld K", marker_to_string (*pcontrol_char), filename, (long) ((*plen) >> 10)); */ if (*pcontrol_char == BLK_ABORTED_BACKUP) { log_to_screen("I can't verify an aborted backup."); retval = 1; } else { retval = 0; } for (i = 1000; i < 1020; i++) { if (tempblock[i] < 32 || tempblock[i] > 126) { tempblock[i] = ' '; } } tempblock[i] = '\0'; log_msg(6, "%s (fname=%s, size=%ld K)", marker_to_string(*pcontrol_char), tempblock + 1000, (long) (*plen) >> 10); paranoid_free(tempblock); return (retval); } /** * Add specified file/slice to the internal catalog of all archives written. * This lets us restart on a new CD/tape/whatever if it runs out of room. We just * write the last [buffer size] MB from the catalog to the new tape, so we know * we have @e all archives on some CD/tape/whatever. * @param type The type of file we're cataloging (afioball, slice, something else) * @param number The fileset number or biggiefile number. * @param aux The slice number if it's a biggiefile, or any other additional info. * @param fn The original full pathname of the file we're recording. * @return The index of the record we just added. */ int register_in_tape_catalog(t_archtype type, int number, long aux, char *fn) { int last; char fname[MAX_TAPECAT_FNAME_LEN]; char *p; p = strrchr(fn, '/'); if (p) { p++; } else { p = fn; } strncpy(fname, p, MAX_TAPECAT_FNAME_LEN); fname[MAX_TAPECAT_FNAME_LEN] = '\0'; last = g_tapecatalog->entries; if (last >= MAX_TAPECATALOG_ENTRIES) { log_it ("Warning - can't log #%d in tape catalog - too many entries already", number); return (-1); } g_tapecatalog->el[last].type = type; g_tapecatalog->el[last].number = number; g_tapecatalog->el[last].aux = aux; g_tapecatalog->el[last].tape_posK = g_tape_posK; strcpy(g_tapecatalog->el[last].fname, fname); g_tapecatalog->entries++; return (last); // returns the index of the record we've jsut added } /** * Decide whether we should start a new tape. This is TRUE if we've run out of tape * (got SIGPIPE) or look like we will. * @param mediasize The size of the tape in megabytes. * @param length_of_incoming_file The length of the file we're about to write, in bytes. * @bug This seems like it'll only work for media_size != autodetect, but Mondo only allows * autodetecting the size. Huh? */ /* BERLIOS: Should be reviewed for mediasize being a off_t ??? */ bool should_we_write_to_next_tape(long mediasize, off_t length_of_incoming_file) { /*@ bool's ***************************************************** */ bool we_need_a_new_tape = FALSE; /*@ end vars *************************************************** */ if (mediasize == 0) { return (FALSE); } if (mediasize > 0 && (g_tape_posK >> 10 >= mediasize)) { log_it("mediasize = %ld", mediasize); we_need_a_new_tape = TRUE; log_to_screen("Should have started a new tape/CD already"); } if ((g_tape_posK + length_of_incoming_file / 1024) >> 10 >= mediasize - (SLICE_SIZE * 4 / 1024)) { log_it("g_tape_posK = %ld\nmediasize = %ld\n", g_tape_posK, mediasize); we_need_a_new_tape = TRUE; } return (we_need_a_new_tape); } /** * Seek through the stream until we find a header block where the NAME field matches * @p the_file_I_was_reading. This is useful if you've just started reading from * a new tape and want to find the file you were reading when the tape ended. * @param the_file_I_was_reading File name to look for. * @return 0 for success, nonzero for failure. */ int skip_incoming_files_until_we_find_this_one(char *the_file_I_was_reading) { char *pA; char *pB; int res; int ctrl_chr; char *temp_fname; char *datablock; long long temp_size, size; long bytes_to_write; datablock = malloc(TAPE_BLOCK_SIZE); malloc_string(temp_fname); pB = strrchr(the_file_I_was_reading, '/'); if (pB) { pB++; } else { pB = the_file_I_was_reading; } log_msg(1, "skip_incoming_..(%s)", pB); log_msg(2, "Looking for initial START_AN_AFIO_OR_SLICE"); ctrl_chr = -1; while (ctrl_chr != BLK_START_AN_AFIO_OR_SLICE) { res = read_header_block_from_stream(&temp_size, temp_fname, &ctrl_chr); if (ctrl_chr == BLK_START_AN_AFIO_OR_SLICE) { break; } log_msg(1, "%lld %s %c", temp_size, temp_fname, ctrl_chr); wrong_marker(BLK_START_AN_AFIO_OR_SLICE, ctrl_chr); log_msg(3, "Still trying to re-sync w/ tape"); } while (ctrl_chr != BLK_START_FILE) { res = read_header_block_from_stream(&temp_size, temp_fname, &ctrl_chr); if (ctrl_chr == BLK_START_FILE) { break; } log_msg(1, "%lld %s %c", temp_size, temp_fname, ctrl_chr); wrong_marker(BLK_START_FILE, ctrl_chr); log_msg(3, "Still trying to re-sync w/ tape"); } pA = strrchr(temp_fname, '/'); if (pA) { pA++; } else { pA = temp_fname; } pB = strrchr(the_file_I_was_reading, '/'); if (pB) { pB++; } else { pB = the_file_I_was_reading; } while (strcmp(pA, pB)) { log_msg(6, "Skipping %s (it's not %s)", temp_fname, the_file_I_was_reading); for (size = temp_size; size > 0; size -= bytes_to_write) { bytes_to_write = (size < TAPE_BLOCK_SIZE) ? (long) size : TAPE_BLOCK_SIZE; // FIXME - needs error-checking and -catching fread(datablock, 1, (size_t) TAPE_BLOCK_SIZE, g_tape_stream); } res = read_header_block_from_stream(&temp_size, temp_fname, &ctrl_chr); if (ctrl_chr != BLK_STOP_FILE) { wrong_marker(BLK_STOP_FILE, ctrl_chr); } res = read_header_block_from_stream(&temp_size, temp_fname, &ctrl_chr); if (ctrl_chr != BLK_STOP_AN_AFIO_OR_SLICE) { wrong_marker(BLK_STOP_AN_AFIO_OR_SLICE, ctrl_chr); } res = read_header_block_from_stream(&temp_size, temp_fname, &ctrl_chr); if (ctrl_chr != BLK_START_AN_AFIO_OR_SLICE) { wrong_marker(BLK_START_AN_AFIO_OR_SLICE, ctrl_chr); } res = read_header_block_from_stream(&temp_size, temp_fname, &ctrl_chr); if (ctrl_chr != BLK_START_FILE) { wrong_marker(BLK_START_FILE, ctrl_chr); } pA = strrchr(temp_fname, '/'); if (pA) { pA++; } else { pA = temp_fname; } pB = strrchr(the_file_I_was_reading, '/'); if (pB) { pB++; } else { pB = the_file_I_was_reading; } } log_msg(2, "Reading %s (it matches %s)", temp_fname, the_file_I_was_reading); paranoid_free(temp_fname); paranoid_free(datablock); return (0); } /** * Start to read from the next tape. Assumes the user has already inserted it. * @param bkpinfo The backup information structure. @c bkpinfo->media_device is the only field used. * @return 0 for success, nonzero for failure. */ int start_to_read_from_next_tape(struct s_bkpinfo *bkpinfo) { /*@ int ********************************************************* */ int res = 0; char *sz_msg; int ctrlchr; long long temp_size; malloc_string(sz_msg); /*@ end vars *************************************************** */ paranoid_pclose(g_tape_stream); system("sync"); system("sync"); system("sync"); log_it("Next tape requested."); insist_on_this_tape_number(g_current_media_number + 1); // will increment it, too log_it("Opening IN the next tape"); if (! (g_tape_stream = open_device_via_buffer(bkpinfo->media_device, 'r', bkpinfo->internal_tape_block_size))) { log_OS_error(g_tape_fifo); log_to_screen("Cannot openin stream device"); return (1); } g_tape_posK = 0; g_sigpipe = FALSE; res += read_header_block_from_stream(&temp_size, sz_msg, &ctrlchr); /* just in case */ if (ctrlchr != BLK_START_OF_TAPE) { wrong_marker(BLK_START_OF_TAPE, ctrlchr); } res += read_header_block_from_stream(&temp_size, sz_msg, &ctrlchr); /* just in case */ if (ctrlchr != BLK_START_OF_BACKUP) { wrong_marker(BLK_START_OF_BACKUP, ctrlchr); } else { log_msg(3, "Next tape opened OK. Whoopee!"); } paranoid_free(sz_msg); return (res); } /** * Start to write to the next tape. Assume the user has already inserted it. * @param bkpinfo The backup information structure. @c bkpinfo->media_device is the only field used. * @return 0 for success, nonzero for failure. */ int start_to_write_to_next_tape(struct s_bkpinfo *bkpinfo) { int res = 0; char command[MAX_STR_LEN * 2]; paranoid_pclose(g_tape_stream); system("sync"); system("sync"); system("sync"); log_it("New tape requested."); insist_on_this_tape_number(g_current_media_number + 1); // will increment g_current_media, too if (g_current_media_number > MAX_NOOF_MEDIA) { res++; log_to_screen("Too many tapes. Man, you need to use nfs!"); } if (bkpinfo->backup_media_type == cdstream) { sprintf(command, "cdrecord -eject dev=%s speed=%d fs=24m -waiti - >> %s 2>> %s", bkpinfo->media_device, bkpinfo->cdrw_speed, MONDO_LOGFILE, MONDO_LOGFILE); log_it("Opening OUT to next CD with the command"); log_it(command); log_it("Let's see what happens, shall we?"); g_tape_stream = popen(command, "w"); if (!g_tape_stream) { log_to_screen("Failed to openout to cdstream (fifo)"); return (1); } } else { log_it("Opening OUT to next tape"); if (! (g_tape_stream = open_device_via_buffer(bkpinfo->media_device, 'w', bkpinfo->internal_tape_block_size))) { log_OS_error(g_tape_fifo); log_to_screen("Cannot openin stream device"); return (1); } } g_tape_posK = 0; g_sigpipe = FALSE; res += write_header_block_to_stream((off_t)0, "start-of-tape", BLK_START_OF_TAPE); /* just in case */ res += write_header_block_to_stream((off_t)0, "start-of-backup", BLK_START_OF_BACKUP); /* just in case */ return (res); } /** * Write a bufferfull of the most recent archives to the tape. The * rationale behind this is a bit complex. If the previous tape ended * suddenly (EOF or otherwise) some archives were probably left in the * buffer. That means that Mondo thinks they have been written, but * the external binary @c buffer has not actually written them. So to * be safe, we start the new tape by writing the last bufferfull from * the old one. This insures that all archives will be on at least * one tape. Sounds inelegant, but it works. * @param bkpinfo The backup information structure. @c bkpinfo->tmpdir is the only field used. * @return 0 for success, nonzero for failure. */ int write_backcatalog_to_tape(struct s_bkpinfo *bkpinfo) { int i, last, res = 0; char *fname; log_msg(2, "I am now writing back catalog to tape"); malloc_string(fname); last = g_tapecatalog->entries - 1; for (i = 0; i <= last; i++) { sprintf(fname, "%s/tmpfs/backcatalog/%s", bkpinfo->tmpdir, g_tapecatalog->el[i].fname); if (!does_file_exist(fname)) { log_msg(6, "Can't write %s - it doesn't exist.", fname); } else { write_header_block_to_stream(length_of_file(fname), "start-backcatalog-afio-or-slice", BLK_START_AN_AFIO_OR_SLICE); log_msg(2, "Writing %s", fname); if (write_file_to_stream_from_file(bkpinfo, fname)) { res++; log_msg(2, "%s failed", fname); } if (i != last) { write_header_block_to_stream((off_t)0, "stop-backcatalog-afio-or-slice", BLK_STOP_AN_AFIO_OR_SLICE); } } } paranoid_free(fname); log_msg(2, "Finished writing back catalog to tape"); return (res); } /** * Write all.tar.gz (produced by Mindi) to the first 32M of the first tape. * @param fname The path to all.tar.gz. * @return 0 for success, nonzero for failure. */ int write_data_disks_to_stream(char *fname) { /*@ pointers *************************************************** */ FILE *fin; char tmp[MAX_STR_LEN]; /*@ long ******************************************************* */ long m = -1; long templong; /*@ int ******************************************************** */ int i, j; /*@ buffers **************************************************** */ char tempblock[256 * 1024]; /*@ end vars *************************************************** */ open_evalcall_form("Writing data disks to tape"); log_to_screen("Writing data disks to tape"); log_it("Data disks = %s", fname); if (!does_file_exist(fname)) { sprintf(tmp, "Cannot find %s", fname); log_to_screen(tmp); return (1); } if (!(fin = fopen(fname, "r"))) { log_OS_error(fname); fatal_error("Cannot openin the data disk"); } for (i = 0; i < 32; i++) { /* 32MB */ for (j = 0; j < 4; j++) { /* 256K x 4 = 1MB (1024K) */ if (!feof(fin)) { m = (long) fread(tempblock, 1, 256 * 1024, fin); } else { m = 0; } for (; m < 256 * 1024; m++) { tempblock[m] = '\0'; } g_tape_posK += fwrite(tempblock, 1, 256 * 1024, g_tape_stream) / 1024; } if (i > g_tape_buffer_size_MB) // otherwise, 'buffer' distorts calculations { templong = ((i - 8) * 4 + j) * 100 / (128 - 8 * 4); update_evalcall_form((int) (templong)); } } paranoid_fclose(fin); close_evalcall_form(); return (0); } /** * Copy @p infile to the opened stream (CD or tape). * @param bkpinfo The backup information structure. @c bkpinfo->media_size is the only field used. * @param infile The file to write to the stream. * @return 0 for success, nonzero for failure. */ int write_file_to_stream_from_file(struct s_bkpinfo *bkpinfo, char *infile) { /*@ buffers **************************************************** */ char tmp[MAX_STR_LEN]; char datablock[TAPE_BLOCK_SIZE]; char checksum[MAX_STR_LEN]; char *infile_basename; /*@ int ******************************************************** */ int retval = 0; int noof_blocks; /* unsigned int ch; */ unsigned int crc16; unsigned int crctt; /*@ pointers *************************************************** */ FILE *fin; char *p; /*@ long ******************************************************* */ long bytes_to_read = 0; long i; off_t filesize; #ifdef EXTRA_TAPE_CHECKSUMS int ch; #endif /*@ initialize ************************************************ */ crc16 = 0; crctt = 0; /*@ end vars *************************************************** */ infile_basename = strrchr(infile, '/'); if (infile_basename) { infile_basename++; } else { infile_basename = infile; } filesize = length_of_file(infile); if (should_we_write_to_next_tape (bkpinfo->media_size[g_current_media_number], filesize)) { start_to_write_to_next_tape(bkpinfo); write_backcatalog_to_tape(bkpinfo); } p = strrchr(infile, '/'); if (!p) { p = infile; } else { p++; } sprintf(tmp, "Writing file '%s' to tape (%ld KB)", p, (long) filesize >> 10); log_it(tmp); write_header_block_to_stream(filesize, infile_basename, BLK_START_FILE); //go_here_to_restart_saving_of_file: if (!(fin = fopen(infile, "r"))) { log_OS_error(infile); return (1); } for (noof_blocks = 0; filesize > 0; noof_blocks++, filesize -= bytes_to_read) { if (filesize < TAPE_BLOCK_SIZE) { bytes_to_read = (long) filesize; for (i = 0; i < TAPE_BLOCK_SIZE; i++) { datablock[i] = '\0'; } } else { bytes_to_read = TAPE_BLOCK_SIZE; } (void) fread(datablock, 1, (size_t) bytes_to_read, fin); g_tape_posK += fwrite(datablock, 1, /*bytes_to_read */ (size_t) TAPE_BLOCK_SIZE, g_tape_stream) / 1024; if (g_sigpipe) { iamhere("Sigpipe occurred recently. I'll start a new tape."); fclose(fin); g_sigpipe = FALSE; start_to_write_to_next_tape(bkpinfo); write_backcatalog_to_tape(bkpinfo); // kinda-sorta recursive :) return (0); } #ifdef EXTRA_TAPE_CHECKSUMS for (i = 0; i < bytes_to_read; i++) { ch = datablock[i]; crc16 = updcrcr(crc16, (unsigned) ch); crctt = updcrc(crctt, (unsigned) ch); } #endif } paranoid_fclose(fin); sprintf(checksum, "%04x%04x", crc16, crctt); /* BERLIOS: what does it do ??? */ write_header_block_to_stream((off_t)g_current_media_number, checksum, BLK_STOP_FILE); // log_it("File '%s' written to tape.", infile); return (retval); } /** * Write a header block to the opened stream (CD or tape). * @param length_of_incoming_file The length to store in the header block. * Usually matters only if this is a file-related header, in which case it should * be the length of the file that will follow. * @param filename The filename to store in the header block. Usually matters * only if this is a file-related header, in which case this should be the name * if the file that will follow. * @param control_char The type of header block this is (start-file, end-file, start-tape, ...) * @return 0 for success, nonzero for failure. */ int write_header_block_to_stream(off_t length_of_incoming_file, char *filename, int control_char) { /*@ buffers **************************************************** */ char tempblock[TAPE_BLOCK_SIZE]; char tmp[MAX_STR_LEN]; char *p; /*@ int ******************************************************** */ int i; off_t olen; /*@ end vars *************************************************** */ olen = length_of_incoming_file; p = strrchr(filename, '/'); /* Make 'em go, "Unnnh!" Oh wait, that was _Master_ P... */ if (!p) { p = filename; } else { p++; } if (!g_tape_stream) { log_to_screen ("You're not backing up to tape. Why write a tape header?"); return (1); } for (i = 0; i < (int) TAPE_BLOCK_SIZE; i++) { tempblock[i] = 0; } sprintf(tempblock + 6000 + control_char, "Mondolicious, baby"); tempblock[7000] = control_char; /* for(i=0;i<8;i++) {tempblock[7001+i]=olen&0xff; olen>>=8;} */ memcpy(tempblock + 7001, (char *) &olen, sizeof(off_t)); /* if (length_of_incoming_file) {memcpy(tempblock+7001,(char*)&length_of_incoming_file,sizeof(long long));} */ strcpy(tempblock + 1000, filename); /* strcpy(tempblock+5555,cksum); */ g_tape_posK += fwrite(tempblock, 1, (size_t) TAPE_BLOCK_SIZE, g_tape_stream) / 1024; sprintf(tmp, "%s (fname=%s, size=%ld K)", marker_to_string(control_char), p, (long) length_of_incoming_file >> 10); log_msg(6, tmp); /* log_tape_pos(); */ return (0); } /** * Log (to screen) an erroneous marker, along with what it should have been. * @param should_be What we were expecting. * @param it_is What we got. */ void wrong_marker(int should_be, int it_is) { /*@ buffer ***************************************************** */ char tmp[MAX_STR_LEN]; /*@ end vars *************************************************** */ sprintf(tmp, "Wrong marker! (Should be %s, ", marker_to_string(should_be)); sprintf(tmp + strlen(tmp), "is actually %s)", marker_to_string(it_is)); log_to_screen(tmp); } /* @} - end of streamGroup */