/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ #ifndef lint static char *sccsid = "@(#)writetape.c 3.0 (ULTRIX) 4/22/86"; #endif lint /**/ /* * * File name: * * writetape.c * * Source file description: * * Tar logic for output to tape functions. * * Functions: * * Usage: * * Compile: * * * Modification history: * ~~~~~~~~~~~~~~~~~~~~ * * revision comments * -------- ----------------------------------------------- * I 19-Dec-85 rjg/ * Create original version. * */ #include "tar.h" /*.sbttl checksum() */ /* Function: * * checksum * * Function Description: * * Perform first-level checksum for file header block. * * Arguments: * * none * * Return values: * * The checksum value is returned. * * Side Effects: * * */ checksum() { /*------*\ Locals \*------*/ STRING_POINTER cp; int i = 0; /*------*\ Code \*------*/ for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++) *cp = ' '; for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++) i += *cp; return (i); }/*E checksum() */ /*.sbttl dorep() replete command line of args */ /* Function: * * dorep * * Function Description: * * Repletes the command line of arguments and places * the files on the output archive. * * Arguments: * * char *argv Pointer to list of path/file name arguments * * Return values: * * * Side Effects: * * n/a */ dorep(argv) char *argv[]; { /*------*\ Locals \*------*/ STRING_POINTER chp; STRING_POINTER chp2; COUNTER i; /*------*\ Code \*------*/ if (CARCH != start_archive && AFLAG) fprintf(stderr,"\n%s: Skipping to %s: %d\n", progname, Archive, start_archive); if (!cflag) { /* Here if doing an add to end or update */ NEXTAU: /**/ /* getdir() will fill in the stats buffer from the file * header block on the input archive. */ getdir(); /* * Save last file name & mod time for possible * archive switch compare. */ if (dblock.dbuf.name[0]) { strcpy(file_name,dblock.dbuf.name); modify_time = stbuf.st_mtime; } do { passtape(); if (term) done(FAIL); /* getdir() fills in the stats buffer from the file * header block on the input archive. */ getdir(); /* * Save last file name & mod time for possible * archive switch compare. */ if (dblock.dbuf.name[0]) { strcpy(file_name,dblock.dbuf.name); modify_time = stbuf.st_mtime; } } while (!endtape()); backtape(); if (EODFLAG && FILE_CONTINUES) goto NEXTAU; OARCH = 0; if (tfile) { sprintf(iobuf, "sort +0 -1 +1nr %s -o %s; awk '$1 != prev {print; prev=$1}' %s >%sX; mv %sX %s", tname, tname, tname, tname, tname, tname); fflush(tfile); system(iobuf); freopen(tname, "r", tfile); fstat(fileno(tfile), &stbuf); high = stbuf.st_size; } }/*E if !cflag */ getcwd(hdir); strcpy(wdir,hdir); while (*argv && !term) { chp2 = *argv; if (!strcmp(chp2,"-C") && argv[1]) { argv++; if ((chdir(*argv)) < 0) perror(*argv); else { getcwd(wdir); strcpy(hdir,wdir); } argv++; continue; }/*E if !strncmp */ for (chp = *argv; *chp; chp++) if (*chp == '/') chp2 = chp; if (chp2 != *argv) { *chp2 = '\0'; if (chdir(*argv) < 0) { perror(*argv); continue; } getcwd(wdir); *chp2 = '/'; chp2++; }/*E if chp2 != *argv */ /* Go put files on the output archive. */ if (putfile(*argv++, chp2, wdir) == A_WRITE_ERR) return(A_WRITE_ERR); if (chdir(hdir) < 0) { fprintf(stderr,"%s: Can't change directory back ?", progname); perror(hdir); done(FAIL); } }/*E while (*argv && ! term) */ PUTE++; for (i=3; i; i--) { if (putempty() == A_WRITE_ERR) return(A_WRITE_ERR); } if (flushtape() == A_WRITE_ERR) return(A_WRITE_ERR); if (VFLAG) { fprintf(stderr,"%s: links = %d/%d directorys = %d/%d/%d\n\n", progname,lcount1,lcount2,dcount1,dcount2,dcount3); } if (!lflag) /* -l: print link resolution errors */ return(SUCCEED); for (; ihead; ihead = ihead->nextp) { if (!ihead->count) continue; fprintf(stderr, "%s: Missing links to: %s\n", progname, ihead->pathname); } return(SUCCEED); }/*E dorep()*/ /*.sbttl getcwd() */ /* Function: * * getcwd * * Function Description: * * Get current working directory character string * * Arguments: * * * Return values: * * * Side Effects: * * */ char *getcwd(buf) char *buf; { int i; /*------*\ Code \*------*/ #ifndef PRO if (!getwd(iobuf)) { fprintf(stderr, "%s: %s\n", progname, iobuf); done(FAIL); } if ((i=strlen(iobuf)) >= NAMSIZ) { fprintf(stderr,"\n%s: File name too long: %s\n",progname,iobuf); done(FAIL); } strcpy(buf,iobuf); #else int pipdes[2]; pipe(pipdes); if (!(i=fork())) { close(1); dup(pipdes[1]); execl("/bin/pwd","pwd",0); perror(progname); printf("\n"); done(FAIL); } while (wait((int *)NULL) != -1) ;/*_NOP_*/ if ((read(pipdes[0],buf,MAXPATHLEN)) < 0) { fprintf(stderr,"%s: getcwd() can't get working directory\n",progname); perror(progname); done(FAIL); } while(*buf != '\n') { buf++; } *buf = '\0'; close(pipdes[0]); close(pipdes[1]); #endif return (buf); }/*E getcwd() */ /*.sbttl putdir() Put directory files on archive */ /* Function: * * putdir * * Function Description: * * Writes directory file entries to the output archive. * * Arguments: * * char *longname Complete pathname+filename * char *shortname File name only * * Return values: * * -1 if error occurs * +n if ok * * Side Effects: * */ putdir(longname,shortname) STRING_POINTER longname; STRING_POINTER shortname; { /*------*\ Locals \*------*/ struct stat Dnode; struct direct dirbuf; SIZE_I i; INDEX j; FLAG dfound = 0; STRING_POINTER dpath; char path[NAMSIZ]; char pathname[NAMSIZ]; INDEX sp = 0; int statval; /*------*\ Code \*------*/ if (!strcmp(longname,".") || !strcmp(longname,"..") || !strcmp(shortname,"/")) return(SUCCEED); new_file = TRUE; if (longname[0] != '/') { /* For relative paths, form an absolute path name * to stat. */ strcpy(pathname,hdir); strcat(pathname,"/"); /* Set the Start Processing flag to skip over leading * absolute path name components. */ sp = strlen(pathname); strcat(pathname,longname); } else strcpy(pathname,longname); i = strlen(pathname); for (j=0; j < i; ) { path[j] = pathname[j++]; if (pathname[j] == '/' || pathname[j] == '\n' || !pathname[j]) { if ( (j >= sp) && (j < NAMSIZ)) { path[j] = 0; #ifndef PRO if (!hflag) statval = lstat(path, &Dnode); else #endif statval = stat(path, &Dnode); if (statval < 0) { fprintf(stderr,"\n%s: putdir() can't stat: %s\n", progname, path); perror(path); return(FAIL); } if ((Dnode.st_mode & S_IFMT) == S_IFDIR) { struct DIRE *direp; for (dfound=0,direp=Dhead; direp; direp = direp->dir_next) { if ((direp->rdev == Dnode.st_rdev) && (direp->inode == Dnode.st_ino)) { dfound++; break; } } if (!dfound) { /* Get some memory for our next * directory list entry. */ direp = (struct DIRE *) malloc(sizeof (*direp)); if (!direp) { /* If no mem, return what we * have & try again ? */ fdlist(); direp = (struct DIRE *) malloc(sizeof (*direp)); if (!direp) { fprintf(stderr,"\n\007%s: putdir() can't get memory for directory list\n", progname); NMEM4D++; return(FAIL); } }/*E if !direp */ /* Save new directory entry */ dcount1++; direp->rdev = Dnode.st_rdev; direp->inode = Dnode.st_ino; direp->dir_next = Dhead; Dhead = direp; /* Lists run backwards */ Dnode.st_size = 0L; Dnode.st_rdev = 0; remaining_chctrs = written = 0L; if (size_of_media[CARCH]) { if ((blocks_used) > (size_of_media[CARCH] - 3L)) OARCH = CARCH; else OARCH = 0; } /* Set the directory path pointer to * start of absolute path name, or to * the start of the relative name * at the end of the absolute path. */ dpath = path + sp; strcat(dpath,"/"); tomodes(&Dnode,1,dpath); sprintf(dblock.dbuf.chksum, "%6o", checksum()); /* Go put directory file on the archive. */ if (writetape((char *)&dblock,1,1,dpath,dpath) == A_WRITE_ERR) return(A_WRITE_ERR); if (vflag && (CARCH >= start_archive)) fprintf(stderr,"a%s %s %s\n", MFLAG ? CARCHS : NULS, dpath, VFLAG ? DIRECT : NULS); }/*E if !dfound*/ }/*T if Dnode.st_mode ..*/ else return(SUCCEED); }/*E if j >= sp .. */ }/*E if pathname[j] ..*/ }/*E for (j=0, d=0; j<i; ) */ return(SUCCEED); }/*E putdir() */ /*.sbttl putfile() Put file(s) on archive recursively */ /* Function: * * putfile * * Function Description: * * This function RECURSIVELY puts files on the output archive. * * Arguments: * * * Return values: * * * Side Effects: * * */ putfile(longname, shortname, parent) STRING_POINTER longname; STRING_POINTER shortname; STRING_POINTER parent; { /*------*\ Locals \*------*/ STRING_POINTER cp; #ifdef U11 STRING_POINTER cp2; #endif struct direct *dp; struct direct dirbuf; #ifndef U11 DIR *dirp; #endif SIZE_I i; FILE_D infile = 0; SIZE_I j; /*------*\ Code \*------*/ new_file = TRUE; if (strlen(longname) >= NAMSIZ) { NAMTL: /**/ fprintf(stderr,"\n%s: Path name too long: %s\n",progname,longname); return(FAIL); } /* -h: Follow symbolic links. Test of user flag. */ #ifndef PRO if (!hflag) i = lstat(shortname, &stbuf); /* Don't follow symlinks - * stat link itself. */ else #endif i = stat(shortname, &stbuf); /* Follow - stat actual file */ if (i < 0) { fprintf(stderr,"\n%s: putfile() can't stat: %s\n", progname, shortname); perror(shortname); return(FAIL); }/*E if i < 0 */ /* (-u) Latest version already in archive? Yes: Skip. */ if (tfile) { if (!update(longname, stbuf.st_mtime)) return(SUCCEED); } /* (-w) Passed user confirmation? No: skip. */ if (wflag) { if (!checkw('r', longname)) return(SUCCEED); } /* (-F[FF]) Recreatable file? Yes: skip for speed. */ if (Fflag) { if (checkf(shortname, stbuf.st_mode, Fflag) == 0) return(SUCCEED); } /* Process directories in path.. */ if (!oflag && !NMEM4D && !DFLAG && !NFLAG) if (putdir(longname,shortname) == A_WRITE_ERR) return(A_WRITE_ERR); switch (stbuf.st_mode & S_IFMT) { case S_IFDIR: { char curd[NAMSIZ]; char newparent[NAMSIZ]; char pfnbuf[NAMSIZ]; getcwd(curd); /* * Copy the long file name to a work buffer and * append a "slash null". * ie.. -> test/0 ... * Set up in order to tack on a file name to a * potential directory name. */ for (i = 0, cp = pfnbuf; *cp++ = longname[i++];) ; /*-NOP-*/ *--cp = '/'; *++cp = 0; /* If no memory for directory lists, or user * doesn't want us to keep them, put out the * directory as in the past. */ if ((NFLAG || DFLAG || NMEM4D) && !oflag) { if (size_of_media[CARCH]) { if ((blocks_used) > (size_of_media[CARCH] - 3L)) OARCH = CARCH; else OARCH = 0; } dcount2++; stbuf.st_size = 0L; stbuf.st_rdev = 0; remaining_chctrs = written = 0L; tomodes(&stbuf,1,pfnbuf); sprintf(dblock.dbuf.chksum, "%6o", checksum()); /* Go put directory file on the archive. */ if (writetape((char *)&dblock,1,1,pfnbuf,pfnbuf) == A_WRITE_ERR) return(A_WRITE_ERR); if (vflag && (CARCH >= start_archive)) fprintf(stderr,"a%s %s %s\n", MFLAG ? CARCHS : NULS, pfnbuf, VFLAG ? DIRECT : NULS); }/*E if NFLAG .. */ i = 0; #ifndef U11 if (chdir(shortname) < 0) { fprintf(stderr, "%s: Can't change directory to: %s\n", progname, shortname); perror(shortname); return(FAIL); } if ((dirp = opendir(".")) == NULL) { fprintf(stderr,"%s: Directory read error: %s\n", progname, longname); perror(longname); if (chdir(curd) < 0) { fprintf(stderr, "%s: Can't change directory back to %s ?\n", progname, curd); perror(curd); } return(FAIL); } getcwd(newparent); while ((dp = readdir(dirp)) && !term) { if (!dp->d_ino) continue; if (!strcmp(".", dp->d_name) || !strcmp("..", dp->d_name)) continue; strcpy(cp, dp->d_name); i = telldir(dirp); closedir(dirp); if (putfile(pfnbuf, cp, newparent) == A_WRITE_ERR) return(A_WRITE_ERR); dirp = opendir("."); seekdir(dirp, i); }/*E while dp = readdir ..*/ closedir(dirp); #endif #ifdef U11 /* Read the directory content. */ if (chdir(shortname) < 0) { fprintf(stderr, "%s: Can't change directory to: %s\n", progname, shortname); perror(shortname); return(FAIL); } infile = open(".",O_RDONLY); if (infile < 0) { fprintf(stderr, "%s: Can't open directory file: %s\n", progname, shortname); perror(shortname); return(FAIL); } getcwd(newparent); while (read(infile, (char *)&dirbuf, sizeof(dirbuf)) > 0 && !term) { if (!dirbuf.d_ino) { i++; continue; } if (strcmp(".", dirbuf.d_name) == 0 || strcmp("..", dirbuf.d_name) == 0) { i++; continue; } cp2 = cp; /* Tack a file name on to a directory name. */ for (j=0; j < DIRSIZ; j++) *cp2++ = dirbuf.d_name[j]; *cp2 = '\0'; close(infile); /* * Buf now should look like - * * test/foo * ^______ and "cp" should point here.... */ if (putfile(pfnbuf,cp,newparent) == A_WRITE_ERR) return(A_WRITE_ERR); infile = open(".",O_RDONLY); i++; lseek(infile, (SIZE_L)(sizeof(dirbuf)*i),0); }/*E while read.. etc*/ close(infile); #endif if (chdir(curd) < 0) { fprintf(stderr, "%s: Can't change directory back to: %s\n", progname, curd); perror(curd); return(FAIL); } break; }/*E case S_IFDIR */ /* */ #ifndef PRO case S_IFLNK: remaining_chctrs = written = 0L; if (size_of_media[CARCH]) { if ((blocks_used + (SIZE_L)blocks) > (size_of_media[CARCH] - 3L)) OARCH = CARCH; else OARCH = 0; } if (stbuf.st_size + 1L >= NAMSIZ) goto NAMTL; /* Should be safe to insert dblock stats. */ stbuf.st_size = 0L; tomodes(&stbuf,1,longname); /* Warning: * The string returned by "readlink" is not null * terminated. The code relies on the fact that * tomodes() zeroes the entire dblock. ergo: by * definition, the linkname in dblock will be * null terminated. */ i = readlink(shortname, dblock.dbuf.linkname, (NAMSIZ - 1)); if (i < 0) { perror(longname); return(FAIL); } dblock.dbuf.typeflag = SYMTYPE; sprintf(dblock.dbuf.chksum, "%6o", checksum()); if (vflag && (CARCH >= start_archive)) { fprintf(stderr,"a%s %s symbolic link to %s\n", MFLAG ? CARCHS : NULS, longname, dblock.dbuf.linkname); } if (writetape((char *)&dblock,1,1, shortname, longname) == A_WRITE_ERR) return(A_WRITE_ERR); break; #endif /* */ case S_IFREG: if ((infile = open(shortname, O_RDONLY)) < 0) { fprintf(stderr, "%s: Can't open file: %s\n", progname, longname); perror(longname); return(FAIL); } if (stbuf.st_nlink > 1) { found = 0; tomodes(&stbuf,1,longname); for (lp = ihead; lp; lp = lp->nextp) if (lp->inum == stbuf.st_ino && lp->devnum == stbuf.st_dev) { found++; break; } /* * Fix for IPR-00006. * If the linked file was already output, * don't output subsequent copies. */ if (found && (!strcmp(dblock.dbuf.name, lp->pathname))) { if (CARCH >= start_archive) fprintf(stderr,"%s: Linked file has already been output. Skipping: %s\n", progname, lp->pathname); close(infile); return(FAIL); } if (found) { strcpy(dblock.dbuf.linkname, lp->pathname); dblock.dbuf.typeflag = LNKTYPE; sprintf(dblock.dbuf.chksum, "%6o", checksum()); if (writetape((char *) &dblock,1,1,shortname,longname) == A_WRITE_ERR) { close(infile); return(A_WRITE_ERR); } if (vflag && CARCH >= start_archive) fprintf(stderr,"a%s %s link to %s\n", MFLAG ? CARCHS : NULS, longname, lp->pathname); lp->count--; close(infile); return(SUCCEED); } lp = (struct linkbuf *) malloc(sizeof(*lp)); if (!lp) { fdlist(); lp = (struct linkbuf *) malloc(sizeof(*lp)); if (!lp && !NMEM4L) { fprintf(stderr,"\n\007%s: Out of memory, link information lost\n", progname); NMEM4L++; } } if (lp) { lcount1++; lp->nextp = ihead; ihead = lp; lp->inum = stbuf.st_ino; lp->devnum = stbuf.st_dev; lp->count = stbuf.st_nlink - 1; strcpy(lp->pathname, longname); } else lcount2++; }/*E if stbuf.st_nlink > 1 */ blocks = (stbuf.st_size + (SIZE_L)(TBLOCK-1L)) / (SIZE_L)TBLOCK; written = 0L; remaining_chctrs = stbuf.st_size; if (size_of_media[CARCH]) { if ((blocks_used + (SIZE_L)blocks) >= (size_of_media[CARCH] - 3L)) OARCH = CARCH; else OARCH = 0; } tomodes(&stbuf,blocks,longname); sprintf(dblock.dbuf.chksum, "%6o", checksum()); /* * Write directory header block for this file. */ if (writetape((char *)&dblock,1,blocks,shortname,longname) == A_WRITE_ERR) { close(infile); return(A_WRITE_ERR); } if (vflag && (CARCH >= start_archive)) fprintf(stderr,"a%s %s %d blocks \n", MFLAG ? CARCHS : NULS, longname, blocks); if ((start_archive - 1) > CARCH) { /* * If skipping archives, try to avoid * reading the file. */ close(infile); for (; blocks > 0; blocks--) { if (writetape(iobuf,1,blocks,shortname,longname) == A_WRITE_ERR) return(A_WRITE_ERR); } } else { while ((i = read(infile, iobuf, TBLOCK)) > 0 && blocks > 0) { written += (SIZE_L)i; if (writetape(iobuf,1,blocks,shortname,longname) == A_WRITE_ERR) { close(infile); return(A_WRITE_ERR); } blocks--; }/*E while i = ..*/ close(infile); if (blocks != 0 || i != 0) fprintf(stderr, "%s: File changed size: %s\n", progname, longname); while (--blocks >= 0) if (putempty()==A_WRITE_ERR) return(A_WRITE_ERR); } break; /* Special files !?! */ case S_IFCHR: case S_IFBLK: remaining_chctrs = written = 0L; if (size_of_media[CARCH]) { if ((blocks_used + (SIZE_L)blocks) > (size_of_media[CARCH] - 3L)) OARCH = CARCH; else OARCH = 0; } stbuf.st_size = 0L; tomodes(&stbuf,1,longname); sprintf(dblock.dbuf.chksum, "%6o", checksum()); if (writetape((char *) &dblock,1,1,shortname,longname) == A_WRITE_ERR) return(A_WRITE_ERR); if (vflag && (CARCH >= start_archive)) fprintf(stderr,"a%s %s (special file)\n", MFLAG ? CARCHS : NULS, longname); break; default: fprintf(stderr, "%s: %s <- Is not a file. Not dumped\n", progname, longname); break; }/*E switch (stbuf.st_mode & S_IFMT) */ return(SUCCEED); }/*E putfile()*/ /*.sbttl tomodes() Put file status in archive header block */ /* Function: * * tomodes * * Function Description: * * Put the file status modes in the tar header block * for this file. * * Arguments: * * struct stat *sp Pointer to filestat structure. * SIZE_I rblocks Number of real blocks to be used by * this file on the archive excluding * the header block. * STRING_POINTER name Pointer to file name string * * Return values: * * * Side Effects: * * */ tomodes(sp,rblocks,name) struct stat *sp; SIZE_I rblocks; STRING_POINTER name; { /*------*\ Locals \*------*/ STRING_POINTER cp; INDEX j; int majordev, minordev; char pwfdat[256]; /*------*\ Code \*------*/ /* Zero out directory header block. */ for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; ) *cp++ = '\0'; /* Insert file stats into directory/header block. */ strcpy(dblock.dbuf.name, name); sprintf(dblock.dbuf.mode, "%6o ", sp->st_mode); sprintf(dblock.dbuf.uid, "%6o ", sp->st_uid); sprintf(dblock.dbuf.gid, "%6o ", sp->st_gid); sprintf(dblock.dbuf.magic, "%6o ",sp->st_rdev); sprintf(dblock.dbuf.size, "%11lo ", sp->st_size); sprintf(dblock.dbuf.mtime, "%11lo ", sp->st_mtime); dblock.dbuf.org_size[0] = ' '; if (!NFLAG) { /* Insert User Group standard format indicator. */ strcpy(dblock.dbuf.magic,TMAGIC); if (getpw(sp->st_uid,pwfdat) && OFLAG) { fprintf(stderr,"\n%s: Can't find user name in password file. UID = %d\n%s: File name = %s\n\n", progname, sp->st_uid, progname, name); } else { /* Insert users' name from password file into * the header block. */ for (j=0;(j<TUNMLEN && pwfdat[j] && pwfdat[j] != ':'); j++) dblock.dbuf.uname[j] = pwfdat[j]; dblock.dbuf.uname[j] = 0; } if (gp=getgrgid(sp->st_gid)) { char *cp; cp = gp->gr_name; for (j=0;(j<TGNMLEN && *cp && *cp != ':'); j++, cp++) dblock.dbuf.gname[j] = *cp; dblock.dbuf.gname[j] = 0; } else { if (OFLAG) fprintf(stderr,"\n%s: Can't find group name in /etc/group file. GID = %d\n%s: File name = %s\n\n", progname, sp->st_gid, progname, name); } switch (sp->st_mode & S_IFMT) { case S_IFDIR: dblock.dbuf.typeflag = DIRTYPE; break; case S_IFCHR: dblock.dbuf.typeflag = CHRTYPE; goto comsd; case S_IFBLK: dblock.dbuf.typeflag = BLKTYPE; comsd: majordev = sp->st_rdev >> 8; minordev = sp->st_rdev & 0377; sprintf(dblock.dbuf.devmajor, "%6o ", majordev); sprintf(dblock.dbuf.devminor, "%6o ", minordev); break; default: dblock.dbuf.typeflag = REGTYPE; break; }/*E switch sp->st_mode & S_IFMT */ /* Put Ultrix archive numbers in the header * unless a User-Group-Standard archive is desired. * ie. no multi-archive extensions. */ if (!SFLAG) { sprintf(dblock.dbuf.carch, "%2d ",CARCH); sprintf(dblock.dbuf.oarch, "%2d ",OARCH); if (size_of_media[CARCH] || (CARCH <= start_archive)) { SIZE_L available_blocks; if (OARCH) { /* * File is being split across an archive. */ sprintf(dblock.dbuf.org_size, "%11lo ",sp->st_size); /* * Determine how much space is left on * this archive and assume one block as a * minimum for any output. */ available_blocks = size_of_media[CARCH] - (blocks_used + 4L); /* Is there at least one block for the header ? */ if (available_blocks >= 0L) { /* * Set continuation header flag. */ header_flag = 1; if (rblocks > available_blocks) chctrs_in_this_chunk = (SIZE_L)TBLOCK * available_blocks; else chctrs_in_this_chunk = (SIZE_L)TBLOCK * rblocks; if (remaining_chctrs >= chctrs_in_this_chunk) remaining_chctrs -= chctrs_in_this_chunk; else /* This will be last chunk. */ chctrs_in_this_chunk = remaining_chctrs; }/*T if available_blocks >= 0 */ else { /* Default chunk size to remaining * chctrs because this file will * start fresh on the next archive. */ chctrs_in_this_chunk = remaining_chctrs; header_flag = 0; }/*F if available_blocks >= 0 */ sprintf(dblock.dbuf.size, "%11lo ", chctrs_in_this_chunk); /* Re-insert mod time because previous * sprintf overwrites p/o mtime field. */ sprintf(dblock.dbuf.mtime, "%11lo ",sp->st_mtime); }/*E if OARCH */ } } }/*E if !NFLAG */ }/*E tomodes() */ /*.sbttl writetape() */ /* Function: * * writetape * * Function Description: * * Top level logic to write an output archive. * * Arguments: * * char *buffer Pointer to data buffer to write from * long blocks Number of blocks to add to those * on the current archive in this call. * long total_blocks Total number of blocks caller would * like to place on the device eventually. * char *longname Files' name. Long and short.. * char *shortname * * Return values: * * TRUE If the number of blocks written to the device * successfully. * * Side Effects: * * When writting data, the number of blocks used is * updated by the number of blocks written. * If an error is detected, the routine exits via the "done" * subroutine. */ writetape(buffer, blocks, total_blocks, shortname, longname) STRING_POINTER buffer; SIZE_I blocks; SIZE_I total_blocks; STRING_POINTER shortname; STRING_POINTER longname; { /*------*\ Locals \*------*/ STRING_POINTER from; STRING_POINTER to; COUNTER i; FLAG write_header; /*------*\ Code \*------*/ if (FEOT) goto WEOA; if (size_of_media[CARCH]) { if (((SIZE_L)blocks + blocks_used) > (size_of_media[CARCH] - 3L)) { /* * PUTE is set by the main-line logic when all * arg files have been output. Its' purpose is * to avoid creating another archive by the coincidence * of file size and media size resulting in tar end * of archive (zero filled) blocks occuring within * the last few blocks of the device. ie. We don't * want to create another archive to simply contain * an end of data (zero filled) block .. */ if (!PUTE) { blocks_used += 3L; MULTI++; /* Continuing to next vol, tack on the EOA blocks. */ if (puteoa() == A_WRITE_ERR) return(A_WRITE_ERR); WEOA: /**/ if (VFLAG && EOTFLAG) fprintf(stderr,"\n%s: %ld Blocks used on %s for %s %d\n", progname, FEOT ? (blocks_used - (SIZE_L)blocks) : (blocks_used - (SIZE_L)nblock), usefile, Archive, CARCH); /* Remember how big this object was for possible * error recovery. For tapes encountering an EOT, 3 * is added to the count because the pseudo writes * on retry "assume" that we will need 3 blocks for * end-of-archive info that really isn't present when * an EOT is seen. */ if (FEOT) size_of_media[CARCH++] = (blocks_used - (SIZE_L)blocks) + 3L; else size_of_media[CARCH++] = EOTFLAG ? (blocks_used - (SIZE_L)nblock) + 3L : blocks_used; close(mt); sprintf(CARCHS, "%d", CARCH); /* If we have encountered a REAL end of tape, there * are nblocks of data waiting to go out. * If the EOT occured during flushtape(), there are * "blocks" worth of data in tbuf to go out. */ if (FEOT) blocks_used = (SIZE_L)blocks; else blocks_used = EOTFLAG ? (SIZE_L)nblock : 0L; if (CARCH > start_archive) { if (CARCH > MAXAR) { fprintf(stderr,"\n%s: Reached maximum %s limit: EXITING\n",progname, Archive); done(FAIL); } /* * Reset media size to default when * resuming real writting on error recovery. */ size_of_media[CARCH] = size_of_media[0]; fprintf(stderr,"\n\007%s: Please change %s media on %s & press RETURN. ", progname, Archive, usefile); response(); } OPENA: /**/ if ((mt = open(usefile, O_RDWR)) < 0) { fprintf(stderr, "\n\007%s: Can't open: %s\n", progname, usefile); perror(usefile); fprintf(stderr,"%s: Please press RETURN to retry ", progname); response(); goto OPENA; } if (OARCH) { write_header = header_flag; recno = 0; if (!EOTFLAG) { /* * __must be end of disk__ */ tomodes(&stbuf, total_blocks,longname); sprintf(dblock.dbuf.chksum, "%6o", checksum()); if (vflag && (CARCH >= start_archive)) fprintf(stderr,"%s: Continuing %D bytes of %s to %s %d\n\n", progname, chctrs_in_this_chunk, longname, Archive, CARCH); MULTI = 0; if (write_header) { if (writetape((char *)&dblock,1,1,shortname,longname) == A_WRITE_ERR) { done(A_WRITE_ERR); return(A_WRITE_ERR); } } }/*E if !EOTFLAG */ if (EOTFLAG) { char lastblock[TBLOCK]; STRING_POINTER cp; int i; /* cremain will be zero in a rare case of a * file ending on an exact EOT boundry. * It does not get split across tapes. */ if (cremain) { if (vflag && (CARCH >= start_archive)) fprintf(stderr,"%s: Continuing %D bytes of %s to %s %d\n\n", progname, cremain, cblock.dbuf.name, Archive, CARCH); /* Save the last "buffered" block. */ #ifdef PRO for (from = (char *)&tbuf[nblock-1], to = (char *)lastblock, i = TBLOCK; i; i--) *to++ = *from++; #else bcopy((char *)&tbuf[nblock-1], lastblock, TBLOCK); #endif /* Shuffle the buffer content down * one block. */ for (from = (char *)&tbuf[nblock-2], to = (char *)&tbuf[nblock-1], i = nblock-1; i; i--) { COUNTER j; for (j = TBLOCK; j; j--) *to++ = *from++; /* Back up the pointers for * the next block. */ to = from - TBLOCK; from = to - TBLOCK; } /* Initialize continuation header block. */ sprintf(cblock.dbuf.carch,"%2d ",CARCH); sprintf(cblock.dbuf.oarch,"%2d ",OARCH); sprintf(cblock.dbuf.size, "%11lo ",cremain); sprintf(cblock.dbuf.mtime, "%11lo ",cmtime); sprintf(cblock.dbuf.org_size, "%11lo ",corgsize); /* Compute new checksum */ for (cp = cblock.dbuf.chksum; cp < &cblock.dbuf.chksum[sizeof(cblock.dbuf.chksum)]; cp++) *cp = ' '; for (cp = cblock.dummy; cp < &cblock.dummy[TBLOCK]; cp++) i += *cp; sprintf(cblock.dbuf.chksum, "%6o", i); /* Copy in continuation header block */ #ifdef PRO for (from = (char *)&cblock, to = (char *)&tbuf[0], i = TBLOCK; i; i--) *to++ = *from++; #else bcopy((char *)&cblock, (char *)&tbuf[0],TBLOCK); #endif }/*E if cremain */ /* Write out the new buffer */ if ((write(mt,tbuf, TBLOCK*nblock)) < 0) goto WERR; if (cremain) { /* Copy the saved lastblock to the start * of the buffer for the next write. */ #ifdef PRO for (from = (char *)lastblock, to = (char *)&tbuf[recno++], i = TBLOCK; i; i--) *to++ = *from++; #else bcopy(lastblock,(char *)&tbuf[recno++],TBLOCK); #endif blocks_used++; }/*E if cremain */ MULTI = EOTFLAG = OARCH = 0; return(SUCCEED); }/*E if EOTFLAG */ }/*if OARCH */ }/*E if !PUTE */ }/*E if (SIZE_L)blocks + blocks_used ..*/ }/*E if size_of_media[CARCH] */ blocks_used += (SIZE_L)blocks; #ifdef PRO for (from = buffer, to = (char *)&tbuf[recno++], i=TBLOCK; i; i--) *to++ = *from++; #else /* * Use the system subroutine call for much better speed ! */ bcopy(buffer, (char *)&tbuf[recno++], TBLOCK); #endif if (recno >= nblock) { if (CARCH >= start_archive) { if ((write(mt, tbuf, TBLOCK*nblock)) < 0 ) { if ((ioctl(mt, MTIOCGET, &mtsts)<0) || size_of_media[CARCH] || (errno != ENOSPC) || NFLAG) { WERR: /**/ fprintf(stderr,"\n"); perror(usefile); fprintf(stderr, "\007%s: Archive %d write error\n", progname, CARCH); done(A_WRITE_ERR); return(A_WRITE_ERR); } else { if (!(mtsts.mt_softstat & MT_EOT)) goto WERR; else { mtops.mt_op = MTCSE; if (ioctl(mt, MTIOCTOP, &mtops) < 0) goto WERR; else { fprintf(stderr,"\n%s: End of %s media", progname, Archive); OARCH = CARCH; EOTFLAG++; MULTI++; goto WEOA; } } } } /* Save possible continuation block when a * new/different file is started. */ if (new_file) { #ifdef PRO for (from = (char *)&dblock, to = (char *)&cblock, i = TBLOCK; i; i--) *to++ = *from++; #else bcopy((char *)&dblock,(char *)&cblock,TBLOCK); #endif new_file = FALSE; } cremain = remaining_chctrs - written; corgsize = stbuf.st_size; cmtime = stbuf.st_mtime; }/*E if (CARCH >= start_archive) */ recno = 0; } return (SUCCEED); }/*E writetape() */