int bbcp_File::Write_All(bbcp_BuffPool &inPool, int nstrms) { bbcp_FileChkSum *csP = 0; bbcp_BuffPool *iBP; pthread_t tid; int rc, csType; // If we have no IOB, then do a simple in-line passthru // if (!IOB) return Passthru(&inPool, &bbcp_CPool, 0, nstrms); // Establish checksum options as well as ordering options. Note that we do // not support checksums in unordered streams and should have been prohibited. // csType = bbcp_Config.csOpts & bbcp_csVerOut ? bbcp_Config.csType:bbcp_csNOP; if (bbcp_Config.csOpts & bbcp_csVerOut || bbcp_Config.Options & bbcp_ORDER) {csP = new bbcp_FileChkSum(&inPool, this, csType, bbcp_Config.csOpts & bbcp_csVerIO,nstrms); nstrms = 1; if ((rc = bbcp_Thread_Start(bbcp_FileCSY, (void *)csP, &tid)) < 0) {bbcp_Emsg("File", rc, "starting file checksum thread."); if (csP) delete csP; return 201; } else iBP = &(csP->csPool); } else iBP = &inPool; // Establish logging options // if (bbcp_Config.Options & bbcp_LOGRD) IOB->Log(0, "DISK"); // Determine what kind of writing we will do here and do it // rc = (blockSize ? Write_Direct(iBP, &inPool, nstrms) : Write_Normal(iBP, &inPool, nstrms)); // Check if we ended because of an error or end of file // if (rc < 0 && rc != -ENOBUFS) bbcp_Emsg("Write", -rc, "writing", iofn); // Check if we should verify a checksum // if (csP) {bbcp_Thread_Wait(tid); if (!rc && csP->csObj) rc = verChkSum(csP); delete csP; } // If checksums are being printed, send off ours if we have it // if (bbcp_Config.csOpts & bbcp_csPrint && *bbcp_Config.csString) cout <<"200 cks: " <<bbcp_Config.csString <<' ' <<iofn <<endl; // Finish up // if (IOB) IOB->Close(); return rc; }
void *bbcp_RTCopy::Lock(bbcp_Semaphore *semP) { struct flock fl; long long lfSize = 1; int rc; // Initialize the lock structure // fl.l_type = F_RDLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; // Indicate that we are all initialized // if (semP) semP->Post(); // Obtain a shared lock on the file // do {rc = fcntl(lkFD, F_SETLKW, &fl);} while(rc < 0 && errno == EINTR); // Check how we ended here. Note that since we are permanent object, thread // locking considerations do not apply here. // if (rc && Grow) {rc = errno; bbcp_Emsg("RTCopy", rc, "locking", lkFN); Grow = -rc; return 0; } // Check if we should verify this copy // if (Grow && bbcp_Config.Options & bbcp_RTCVERC) {lfSize = FSp->getSize(lkFD); if (lfSize < 0) {rc = static_cast<int>(-lfSize); bbcp_Emsg("RTCopy", rc, "stating", lkFN); Grow = -rc; return 0; } if (!lfSize) {Grow = -EADV; return 0;} } // Obtain the final size of the source file // if ((xLim = FSp->getSize(ioFD)) < 0) Grow = static_cast<int>(xLim); else Grow = 0; // All done // return 0; }
void bbcp_LogFile::Record(bbcp_LogFiler *lrP) { static bbcp_Mutex logMutex; bbcp_Timer Mytime; bbcp_Stream inData; char *inLine, tbuff[24]; struct iovec iolist[3] = {{(caddr_t)tbuff, 0}, {0,0}, {(char *)"\n", 1}}; int LogFD = lrP->LogF->Logfd; // Attach the file descriptor to the stream // inData.Attach(lrP->ErFD); // Get a full line from the stream to avoid line splittage in the log and // write it out to the lof file adding appropriate headers. // while((inLine = inData.GetLine())) {if (!(*inLine)) continue; logMutex.Lock(); if (LogFD) {tbuff[0] = '\0'; iolist[0].iov_len = Mytime.Format(tbuff); iolist[1].iov_base = inLine; iolist[1].iov_len = strlen(inLine); if (writev(LogFD, (const struct iovec *)&iolist, 3) < 0) {bbcp_Emsg("LogFile",errno,"writing log to",lrP->LogF->Logfn); LogFD = 0; } } else cerr <<inLine <<endl; logMutex.UnLock(); } }
int bbcp_Stream::Attach(int FileDescriptor, int bsz) { // Close the current stream. Close will handle unopened streams. // Close(); // Allocate a new buffer for this stream // if (!bsz) buff = 0; else if (!(buff = (char *)malloc(bsz+1))) return bbcp_Emsg("Attach", errno, "allocating stream buffer."); // Initialize the stream // FD = FE= FileDescriptor; bnext = buff; bsize = bsz+1; bleft = 0; recp = 0; token = 0; flags = 0; ecode = 0; xcont = 1; xline = 0; return 0; }
int bbcp_Protocol::SendArgs(bbcp_Node *Node, bbcp_FileSpec *fsp, char *cbhost, int cbport, char *addOpt) { char buff[512], *apnt[6]; int alen[6], i = 0; // The remote program should be running at this point, setup the args // if (bbcp_Config.CopyOpts) {apnt[i] = bbcp_Config.CopyOpts; alen[i++] = strlen(bbcp_Config.CopyOpts); } if (addOpt) {apnt[i] = addOpt; alen[i++] = strlen(addOpt);} apnt[i] = buff; alen[i++] = snprintf(buff, sizeof(buff)-1, " -H %s:%d\n", cbhost, cbport); apnt[i] = 0; alen[i] = 0; // Send the argumnets via the stdout/stdin stream for the node // if (Node->Put(apnt, alen) < 0) return bbcp_Emsg("Protocol", errno, "sending arguments to", Node->NodeName()); // Send the file arguments now // apnt[1] = (char *)"\n"; alen[1] = 1; apnt[2] = 0; alen[2] = 0; while(fsp) {apnt[0] = fsp->pathname; alen[0] = strlen(fsp->pathname); if (Node->Put(apnt, alen) < 0) return bbcp_Emsg("Protocol", errno, "sending file arguments to", Node->NodeName()); fsp = fsp->next; } // Send eol // apnt[0] = (char *)"\0"; alen[0] = 1; apnt[1] = 0; alen[1] = 0; if (Node->Put(apnt, alen) < 0) return bbcp_Emsg("Protocol", errno, "sending eol to", Node->NodeName()); // All done // return 0; }
int bbcp_Node::Outgoing(bbcp_Protocol *protocol) { int i, retc; bbcp_Link *link; // Establish the control connection first // if (link = bbcp_Net.Connect(bbcp_Config.CBhost, bbcp_Config.CBport, 3)) if ((retc = protocol->Login(link, 0)) < 0) {delete link; link = 0;} // Now establish all data channels if we have a control channel // if (link) {pthread_t tid, T_id[BBCP_MAXSTREAMS]; // Start threads for data connections // for (i = 0; i < bbcp_Config.Streams; i++) {if ((retc=bbcp_Thread_Start(bbcp_Connect,(void *)protocol,&tid))<0) {bbcp_Emsg("Outgoing", retc, "starting connect thread"); _exit(100); } T_id[i] = tid; #ifdef CRAY_X1E usleep(1); #endif } for (i = 0; i < bbcp_Config.Streams; i++) {if (!(link = (bbcp_Link *)bbcp_Thread_Wait(T_id[i]))) break; link->LinkNum = dlcount; data_link[dlcount++] = link; } } iocount = dlcount; // Make sure we have all of the required links // if (dlcount < bbcp_Config.Streams) return Recover("Connect"); // Determine what the actual window size is (only if verbose) // if (bbcp_Config.Options & bbcp_BLAB) chkWsz(data_link[0]->FD()); // Initialize the buddy pipeline; a patented way of ensuring maximum parallelism // if (dlcount > 1 && (bbcp_Config.Options & (bbcp_SRC|bbcp_ORDER))) {i = dlcount-1; data_link[i]->setBuddy(data_link[0]); while(i--) data_link[i]->setBuddy(data_link[i+1]); bbcp_Link::setNudge(); } return 0; }
int bbcp_Protocol::Request_login(bbcp_Link *Net) { const char *CtlLogin = "******"; const char *DatLogin = "******"; char buff[512], *id, *wp; int retc, blen; bbcp_Login_Stream loginStream(Net); bbcp_Node *np = loginStream.np; // Determine wether this is a control or data path // id = (Remote ? (char *)DatLogin : (char *)CtlLogin); // Prepare the login request // blen = sprintf(buff,id,bbcp_Config.SecToken,(bbcp_Net.AutoTune() ? "+" : ""), bbcp_Config.Wsize, bbcp_Version.VData, bbcp_Config.RWBsz); // Send the request // if ((retc = np->Put(buff, (ssize_t)blen)) < 0) return bbcp_Emsg( "Request_Login",-(np->LastError()), "requesting", id, (char *)"path."); // If this is a data stream, then tell caller to hold on to the net link // if (Remote) {np->Detach(); return 0;} // For a control connection, read the acknowledgement below // nnn loginok wsz: <num> [<dsz>] // if (np->GetLine()) {if (np->GetToken() && (wp = np->GetToken()) && !strcmp(wp, "loginok") && (wp = np->GetToken()) && !strcmp(wp, "wsz:") && (wp = np->GetToken()) && AdjustWS(wp, np->GetToken(), 1)) {Remote = np; loginStream.np = 0; return 1; } } // Invalid response // return bbcp_Fmsg("Request_Login", "Invalid login ack sequence."); }
bbcp_ZCX *bbcp_Node::setup_CX(int deflating, int iofd) { int retc, clvl; bbcp_BuffPool *ibp, *rbp, *obp; bbcp_ZCX *cxp; pthread_t tid; // Initialize the processing parameters // if (deflating) {ibp = &bbcp_APool; rbp = &bbcp_APool; obp = &bbcp_BPool; clvl = (bbcp_Config.Complvl ? bbcp_Config.Complvl : 1); } else { ibp = &bbcp_CPool; rbp = &bbcp_BPool; obp = &bbcp_APool; clvl = 0; } // Allocate buffers in the A pool // if (bbcp_APool.Allocate(bbcp_Config.BNum, bbcp_Config.RWBsz, !deflating)) return 0; // Allocate a new compression/expansion object // cxp = new bbcp_ZCX(ibp, rbp, obp, clvl, iofd, (int)(bbcp_Config.Options & (clvl ? bbcp_LOGCMP : bbcp_LOGEXP))); // Start the compression/expansion thread // if ((retc = bbcp_Thread_Start(bbcp_doCX, (void *)cxp, &tid))<0) {bbcp_Emsg("File", retc, "starting", (char *)(deflating ? "compression" : "expansion"), (char *)" thread."); _exit(100); } cxp->TID = tid; DEBUG("Thread " <<tid <<" assigned to cx stage."); // Return the compression object // return cxp; }
int bbcp_Stream::Put(const char *data, int dlen) { int dcnt = dlen, retc; if (flags & bbcp_Stream_BUSY) {ecode = ETXTBSY; return -1;} while(dcnt) {do { retc = write(FE, (const void *)data, (size_t)dlen);} while (retc < 0 && errno == EINTR); if (retc >= 0) dcnt -= retc; else {flags |= bbcp_Stream_BUSY; bbcp_Emsg("Put", errno, "writing to stream."); flags &= ~bbcp_Stream_BUSY; return -1; } } return 0; }
void bbcp_LogFile::Monitor(int fdnum, char *fdname) { bbcp_LogFiler *lrP = new bbcp_LogFiler(this, fdname, fdnum); int retc; // Start a log file thread (we loose storage upon failure) // if ((retc = bbcp_Thread_Run(bbcp_FileLog, (void *)lrP, &(lrP->LogT)))) {bbcp_Emsg("LogFile", errno, "start logging thread to", Logfn); return; } // Chain this logger into out list of loggers // Flog.Lock(); lrP->Next = Loggers; Loggers = lrP; Flog.UnLock(); DEBUG("Thread " <<lrP->LogT <<" assigned to logging " <<fdname); }
void bbcp_Protocol::putCSV(char *Host, char *csFn, char *csVal, int csVsz) { //1234567890123 struct iovec iov[] = {{(char *)"Checksum: ", 10}, {bbcp_Config.csName,strlen(bbcp_Config.csName)}, {(char *)" ", 1}, {csVal, csVsz}, {(char *)" ", 1}, {Host, strlen(Host)}, {(char *)":", 1}, {csFn, strlen(csFn)}, {(char *)"\n",1}}; int n = sizeof(iov)/sizeof(iov[0]); // Write the checksum to a special file if it exists // if (bbcp_Config.csPath) {if (writev(bbcp_Config.csFD, iov, n) < 0) {bbcp_Emsg("Protocol",errno,"writing checksum to",bbcp_Config.csPath); close(bbcp_Config.csFD); bbcp_Config.csFD = -1; } } else writev(STDERR_FILENO, iov, n); }
int bbcp_LogFile::Open(const char *fname) { // Check if we have a logfile already // if (Logfd >= 0) return -ETXTBSY; // Open the log file // if ((Logfd = open(fname, O_WRONLY | O_CREAT | O_APPEND | O_DSYNC, 0644)) < 0) return bbcp_Emsg("LogFile", -errno, "opening", fname); // Set up for logging // Logfn = strdup(fname); // All done // return 0; }
int bbcp_Stream::Put(char *datavec[], int dlenvec[]) { int i, retc, dlen; const char *data; if (flags & bbcp_Stream_BUSY) {ecode = ETXTBSY; return -1;} for (i = 0; datavec[i]; i++) {data = datavec[i]; dlen = dlenvec[i]; while(dlen) {do { retc = write(FE, (const void *)data, (size_t)dlen);} while (retc < 0 && errno == EINTR); if (retc >= 0) {data += retc; dlen -= retc;} else {flags |= bbcp_Stream_BUSY; bbcp_Emsg("Put", errno, "writing to stream."); flags &= ~bbcp_Stream_BUSY; return -1; } } } return 0; }
int bbcp_Link::Control_In(bbcp_Buffer *bp) { int newsz; bbcp_Header *hp = &bp->bHdr; // Check if this is a vanilla close request. // if (hp->cmnd == (char)BBCP_CLOSE) {DEBUG("Close request received on link " <<LinkNum); bp->blen = 0; bbcp_BPool.putFullBuff(bp); return 0; } // Check if this is a checksum close request. // if (hp->cmnd == (char)BBCP_CLCKS) {DEBUG("Checksum close request received on link " <<LinkNum); if (bbcp_Config.csOpts & bbcp_csSend) {DEBUG("Setting checksum from link " <<LinkNum); bbcp_Config.setCS(bp->bHdr.cksm); bp->blen = 0; } bbcp_BPool.putFullBuff(bp); return 0; } // Check if this is an abort equest. If so, queue the message and terminate // if (hp->cmnd == (char)BBCP_ABORT) {DEBUG("Abort request received on link " <<LinkNum); bbcp_BPool.putFullBuff(bp); return -1; } // Unknown command here // return bbcp_Emsg("Control_In", EINVAL, "invalid command code", Lname); }
int bbcp_RTCopy::Start(bbcp_FileSystem *fsp, const char *iofn, int iofd) { bbcp_Semaphore xSem(0); int rc; // Initialize the common variables // ioFD = iofd; Grow = 1; Left = 0; Blok = (bbcp_Config.Options & bbcp_RTCBLOK ? bbcp_Config.Streams : 0); FSp = fsp; // Initialize variable dependent on how we will do locking // if (bbcp_Config.rtLockf) {lkFN = bbcp_Config.rtLockf; lkFD = bbcp_Config.rtLockd; } else { lkFN = iofn; lkFD = dup(iofd); } // Now start a thread that will try to obtain a shared lock // if ((rc = bbcp_Thread_Run(bbcp_RTCopyLK, (void *)&xSem, &Tid)) < 0) {bbcp_Emsg("RTCopy", rc, "starting file r/t lock thread."); Grow = -rc; return 0; } // Wait for the thread to set up // xSem.Wait(); return (Grow >= 0); }
int bbcp_Protocol::Process(bbcp_Node *Node) { bbcp_FileSpec *fp = bbcp_Config.srcSpec; pthread_t Tid; int rc, NoGo = 0; char *cp; // If there is a r/t lock file, make sure it exists // if ((bbcp_Config.Options & bbcp_RTCSRC) && bbcp_Config.rtLockf && (bbcp_Config.rtLockd = open(bbcp_Config.rtLockf, O_RDONLY)) < 0) {rc = errno, NoGo = 1; bbcp_Emsg("Config", rc, "opening lock file", bbcp_Config.rtLockf); } // Make sure all of the source files exist at this location. If there is an // error, defer exiting until after connecting to prevent a hang-up. We // make sure that we are not trying to copy a directory. // while(fp) {NoGo |= fp->Stat(); if (fp->Info.Otype == 'd' && !(bbcp_Config.Options & bbcp_RECURSE)) {bbcp_Fmsg("Source", fp->pathname, "is a directory."); NoGo = 1; break; } fp = fp->next; } // If this is a recursive list, do it in the bacground while we try to connect. // This avoids time-outs when large number of files are enumerated. // if (!NoGo && bbcp_Config.Options & bbcp_RECURSE) if ((rc = bbcp_Thread_Start(bbcp_ProtocolLS, 0, &Tid)) < 0) {bbcp_Emsg("Protocol", rc, "starting file enumeration thread."); NoGo = 1; } // Establish all connections // if (Node->Start(this, (bbcp_Config.Options & bbcp_CON2SRC)) || Node->getBuffers(0)) return 2; Local = Node; // At this point, if we're running with the -r recursive option, our list of // file specs (bbcp_Config.srcSpec) is being extended recursively to include // all subdirs and their contents. We must wait for the thread to finish. // if (!NoGo && bbcp_Config.Options & bbcp_RECURSE) bbcp_Thread_Wait(Tid); // If there was a fatal error, we can exit now, the remote side will exit // if (NoGo) {char buff[8]; strcpy(buff, "eol\n"); Remote->Put(buff, (ssize_t)4); Node->Stop(); return 2; } rc = 0; // Process all control connection requests and return // while(!rc && Remote->GetLine()) {if (!(cp = Remote->GetToken())) continue; if (!strcmp(cp, "flist")) rc = Process_flist(); else if (!strcmp(cp, "get")) rc = Process_get(); else if (!strcmp(cp, "exit")) {rc = Process_exit(); break;} else {bbcp_Fmsg("Process", "Invalid command, '", cp, "'."); rc = 1; } } // Dismantle this node and return // Node->Stop(); if (cp) return rc; bbcp_Fmsg("Source", "Unexpected end of control stream from", Remote->NodeName()); return 32; }
char *bbcp_Stream::GetLine() { int bcnt, retc; char *bp; // Check if end of message has been reached. // if (flags & bbcp_Stream_EOM) return (char *)NULL; // Find the next record in the buffer // if (bleft > 0) {recp = bnext; bcnt = bleft; for (bp = bnext; bcnt--; bp++) if (!*bp || *bp == '\n') {if (!*bp) flags |= bbcp_Stream_EOM; *bp = '\0'; bnext = ++bp; bleft = bcnt; token = recp; return recp; } else if (notabs && *bp == '\t') *bp = ' '; // There is no next record, so move up data in the buffer. // strncpy(buff, bnext, bleft); bnext = buff + bleft; } else bnext = buff; // Prepare to read in more data. // bcnt = bsize - (bnext - buff) -1; bp = bnext; // Read up to the maximum number of bytes. Stop reading should we see a // new-line character or a null byte -- the end of a record. // ecode = 0; recp = token = buff; // This will always be true at this point while(bcnt) {do { retc = read(FD, (void *)bp, (size_t)bcnt); } while (retc < 0 && errno == EINTR); if (retc < 0) {bbcp_Emsg("GetLine", errno, "reading request."); return (char *)0;} if (!retc) {*bp = '\0'; flags |= bbcp_Stream_EOM; bnext = ++bp; bleft = 0; return buff; } bcnt -= retc; while(retc--) if (!*bp || *bp == '\n') {if (!*bp) flags |= bbcp_Stream_EOM; else *bp = '\0'; bnext = ++bp; bleft = retc; return buff; } else { if (notabs && *bp == '\t') *bp = ' '; bp++; } } // All done, force an end of record. // bbcp_Emsg("GetLine", EMSGSIZE, "record truncated."); buff[bsize-1] = '\0'; return buff; }
int bbcp_Stream::Exec(char **parm, int inrd, int inerr) { int fildes_In[2], fildes_Out[2] = {-1,-1}, fildes_Err[2] = {0,0}; int retc, Child_Out = FD, Child_In = FE, Child_Err = 0; // Wait for any previous command to finish on this stream // Drain(); // Create a pipe if we have no attached FD. Recall that FD is the // descriptor we read from and FE is the one we write to. This is // true for sockets. For pipes, the relationship is reversed in // the child. Plus, we need to get two pipes if the child's STDIN // is to be redirected. This is because pipes suffer from backflow. // if (FD < 0) {if (pipe(fildes_Out) || (inrd && pipe(fildes_In)) || (inerr && pipe(fildes_Err))) return bbcp_Emsg("Exec",errno,"creating a pipe for",parm[0]); Child_In=fildes_In[0]; Child_Out=fildes_Out[1]; Child_Err=fildes_Err[1]; fildes_Out[1] = (inrd ? fildes_In[1] : -1); if (retc = Attach(fildes_Out)) return retc; } // Fork a process first so we can pick up the next request. // if ((child = bbcp_OS.Fork()) != 0) {close(Child_Out); retc = -errno; if (inrd) close(Child_In); if (inerr) close(Child_Err); if (child > 0) return fildes_Err[0]; retc=bbcp_Emsg("Exec", retc,"forking request process for",parm[0]); close(fildes_In[1]); if (inrd) close(fildes_Out[0]); if (inerr) close(fildes_Err[0]); return retc; } /*****************************************************************/ /* C h i l d P r o c e s s */ /*****************************************************************/ // Close the parent end of the pipe // if (fildes_In[1] >= 0) close(fildes_In[1]); if (fildes_Out[0] >= 0) close(fildes_Out[0]); if (fildes_Err[0] > 0) close(fildes_Err[0]); // Redirect standard in if so requested // if (inrd) {if (dup2(Child_In, STDIN_FILENO) < 0) {bbcp_Emsg("Exec", errno, "setting up standard in for", parm[0]); exit(255); } else close(Child_In); } // Reassign the stream to be standard out to capture all of the output. // if (dup2(Child_Out, STDOUT_FILENO) < 0) {bbcp_Emsg("Exec", errno, "setting up standard out for", parm[0]); exit(255); } else close(Child_Out); // Reassign the stream to be standard err to capture all of the output. // if (inerr && Child_Err) {if (dup2(Child_Err, STDERR_FILENO) < 0) {bbcp_Emsg("Exec", errno, "setting up standard err for", parm[0]); exit(255); } else close(Child_Err); } // Invoke the command never to return // DEBUG("PATH=" <<getenv("PATH")); execvp(parm[0], parm); bbcp_Emsg("Exec", errno, "executing", parm[0], parm[1]); exit(255); return(255); // some compilers demand a return in int functions }
int bbcp_Link::Buff2Net() { bbcp_Buffer *outbuff; ssize_t wrsz, wlen = 0; const ssize_t hdrsz = (ssize_t)sizeof(bbcp_Header); int retc = 0, NotDone = 1, csLen = (csObj ? csObj->csSize() : 0); struct iovec iov[2] = {0, hdrsz, 0, 0}; // Establish logging options // if (bbcp_Config.Options & bbcp_LOGOUT) IOB.Log(0, "NET"); // Do this until an error of eof // while(NotDone) { // Obtain a buffer // if (!(outbuff = bbcp_BPool.getFullBuff())) {NotDone = -1; retc = ENOBUFS; break;} // Compose the header and see if control operation required // if (outbuff->blen <= 0) {if ((NotDone = Control_Out(outbuff)) < 0) {retc = 255; break;}} else bbcp_BPool.Encode(outbuff, BBCP_IO); // Check if we should generate a checksum // if (csObj && outbuff->blen) memcpy(outbuff->bHdr.cksm, csObj->Calc(outbuff->data,outbuff->blen), csLen); // Write all of the data (header & data are sequential) // iov[0].iov_base = (char *)&outbuff->bHdr; iov[1].iov_base = outbuff->data; iov[1].iov_len = outbuff->blen; wrsz = (ssize_t)outbuff->blen + hdrsz; if ((wlen = IOB.Write(iov, 2)) != wrsz) break; // Queue buffer for re-use // if (NotDone) bbcp_BPool.putEmptyBuff(outbuff); else bbcp_BPool.putFullBuff(outbuff); outbuff = 0; // Tell our buddy that it's ok to continue then do a rendezvous // if (Nudge) {Buddy->Rendezvous.Post(); if (Wait) Rendezvous.Wait();} } // Check how we ended this loop // if (outbuff) bbcp_BPool.putEmptyBuff(outbuff); if (NotDone > 0 && !wlen) {bbcp_BPool.Abort(); if (wlen < 0) retc=bbcp_Emsg("Link",-wlen,"writing data for",Lname); else if (wlen > 0) {bbcp_Fmsg("Link","Data lost on link", Lname); retc = 100; } } else if (NotDone) bbcp_BPool.Abort(); // All done // if (Nudge) {Wait = 0; Buddy->Rendezvous.Post();} return (retc < 0 ? -retc : retc); }
int bbcp_Node::Run(char *user, char *host, char *prog, char *parg) { static char ipv4[] = {'-','4','\0'}; int fderr, numa = 0; char *username, *sshDest, bufDest[264], *Argv[1024], *ap, *pp = prog; const int ArgvSize = sizeof(Argv)/sizeof(char *)-2; // Free up any node name here // if (nodename) free(nodename); nodename = strdup(host ? host : bbcp_Config.MyHost); username = (user ? user : bbcp_Config.MyUser); // Check for an IPV6 address as ssh does not follow the rfc standard // if (*nodename != '[') sshDest = nodename; else {int i = strlen(nodename); if (i > (int)sizeof(bufDest)) return -EHOSTUNREACH; strcpy(bufDest, nodename+1); bufDest[i-2] = 0; sshDest= bufDest; } // Break up the command line and perform substitutions // if (!(user || host)) {Argv[0] = bbcp_Config.MyProg; numa = 1;} else for (numa = 0; *pp && numa < ArgvSize; numa++) {while(*pp && *pp == ' ') pp++; ap = pp; while(*pp && *pp != ' ') pp++; if (*pp) {*pp = '\0'; pp++;} if (*ap == '%' && !ap[2]) { if (ap[1] == 'I') {if (bbcp_Config.IDfn) {Argv[numa++] = (char *)"-i"; Argv[numa] = bbcp_Config.IDfn;} else numa--; } else if (ap[1] == 'U') Argv[numa] = username; else if (ap[1] == 'H') Argv[numa] = sshDest; else if (ap[1] == '4') {if (bbcp_Config.Options & bbcp_IPV4) Argv[numa] = ipv4; else numa--; } else Argv[numa] = ap; } else Argv[numa] = ap; } // Complete argument list to start the actual copy program // if (numa >= ArgvSize) return bbcp_Emsg("Run", -E2BIG, "starting", prog); Argv[numa++] = parg; Argv[numa] = 0; // Invoke the program // if ((fderr=NStream.Exec(Argv, 1, bbcp_Config.MLog != 0)) < 0) return -fderr; // Perform logging function here // if (bbcp_Config.MLog) bbcp_Config.MLog->Monitor(fderr, parg); // Perform debugging here // if (DEBUGON) {int i; cerr <<"bbcp_" <<bbcp_Debug.Who <<": Running as pid " <<NStream.getPID() <<": "; for (i = 0; i < numa; i++) if (Argv[i]) cerr <<Argv[i] <<' '; cerr <<endl; } // All done // return 0; }
int bbcp_Link::Net2Buff() { static const char *Etxt[] = {"", "Invalid header length", // 1 "Invalid buffer length", // 2 "Invalid data length", // 3 "Invalid checksum", // 4 "Invalid hdr checksum" // 5 }; enum err_type {NONE = 0, IHL = 1, IBL = 2, IDL = 3, ICS = 4, IHS = 5}; err_type ecode = NONE; bbcp_Buffer *inbuff; ssize_t rdsz, rlen = 0; ssize_t hdrsz = (ssize_t)sizeof(bbcp_Header); int maxrdsz = bbcp_BPool.DataSize(); int i, notdone = 1, csLen = (csObj ? csObj->csSize() : 0); // Establish logging options // if (bbcp_Config.Options & bbcp_LOGIN) IOB.Log("NET", 0); // Do this until an error of eof // while(notdone) { // Obtain a buffer // if (!(inbuff = bbcp_BPool.getEmptyBuff())) {rlen = ENOBUFS; notdone = 0; break;} // Read the header information into the header buffer // if ((rlen = IOB.Read((char *)&inbuff->bHdr, hdrsz)) != hdrsz) {if (rlen > 0) {ecode = IHL; rlen = EINVAL;} break;} // Decode the header and make sure it decoded correctly // if (!bbcp_BPool.Decode(inbuff)) {ecode = IHS; break;} // Make sure the read length does not overflow our buffer // if ((rdsz = inbuff->blen) > maxrdsz) {ecode = IBL; break;} // Read data into the buffer and do checksum if needed // if (rdsz) {if ((rlen = IOB.Read(inbuff->data, rdsz)) != rdsz) {if (rlen > 0) ecode = IDL; break;} if (csObj && memcmp(csObj->Calc(inbuff->data,inbuff->blen), inbuff->bHdr.cksm,csLen)) {ecode = ICS; break;} } // Check if this is a control operation or data operation // if (inbuff->bHdr.cmnd == (char)BBCP_IO) bbcp_BPool.putFullBuff(inbuff); else if ((notdone = Control_In(inbuff)) <= 0) break; // Tell our buddy that it's ok to continue then do a rendezvous // if (Nudge) {Buddy->Rendezvous.Post(); if (Wait) Rendezvous.Wait();} } // If we ended the loop with an error, abort the buffer pool to force all // threads dependent on the queue to abnormally terminate. Otherwise, post // the buddy thread twice since that is the most that it may need to read. // if (Nudge) {Wait = 0; i = bbcp_Config.Streams; do {Buddy->Rendezvous.Post();} while(i--); } if (notdone) {bbcp_BPool.Abort(); if (ecode != NONE) {bbcp_Fmsg("Net2Buff", Etxt[(int)ecode], "from", Lname); return 128; } if (rlen >=0) return EPIPE; bbcp_Emsg("Net2Buff", rlen, "reading data from", Lname); return -rlen; } return 0; }
int bbcp_Node::RecvFile(bbcp_FileSpec *fp, bbcp_Node *Remote) { static const int wOnly = S_IWUSR; const char *Args = 0, *Act = "opening", *Path = fp->targpath; long tretc = 0; int i, oflag, retc, Mode = wOnly, progtid = 0; long long startoff = 0; pid_t Child[2] = {0,0}; bbcp_File *outFile, *seqFile = 0; bbcp_ZCX *cxp = 0; pthread_t tid, link_tid[BBCP_MAXSTREAMS+4]; bbcp_Timer Elapsed_Timer; bbcp_ProgMon *pmp = 0; float CRatio; // Perform Force or Append processing // if (bbcp_Config.Options & bbcp_XPIPE) {oflag = O_WRONLY; Path = bbcp_Config.snkSpec->pathname; Args = bbcp_Config.snkSpec->fileargs; if (bbcp_Config.snkSpec->Info.Otype != 'p') Act = "running"; else {Mode |= S_IFIFO; if (Args) {bbcp_Fmsg("RecvFile", "Spaces disallowed in named output pipe",Path); return -EINVAL; } } } else if (bbcp_Config.Options & bbcp_FORCE) {if (!(bbcp_Config.Options & bbcp_NOUNLINK)) fp->FSys()->RM(Path); oflag = O_WRONLY | O_CREAT | O_TRUNC; } else if (bbcp_Config.Options & bbcp_APPEND) {if (retc = fp->WriteSigFile()) return retc; if (startoff = fp->targetsz) oflag = O_WRONLY; else oflag = O_CREAT | O_WRONLY; } else oflag = O_WRONLY | O_CREAT | O_EXCL; // Establish mode, we normally make the file write-only // if ( bbcp_Config.Options & bbcp_RTCSNK && !(bbcp_Config.Options & (bbcp_RTCHIDE|bbcp_XPIPE))) Mode = bbcp_Config.Mode|S_IWUSR|S_ISUID; // Tell the user what we are bout to do // if (bbcp_Config.Options & bbcp_BLAB | bbcp_Config.Progint) if (bbcp_Config.Options & bbcp_APPEND) {char buff[32]; sprintf(buff, "%lld", startoff); bbcp_Fmsg("RecvFile","Appending to",Path,"at offset",buff); } else bbcp_Fmsg("RecvFile", "Creating", Path); else DEBUG("Receiving " <<fp->pathname <<" as " <<Path <<" offset=" <<startoff); // Receive the file in a sub-process so that we don't muck with this one // if ((Child[0] = bbcp_OS.Fork()) < 0) return bbcp_Emsg("RecvFile", errno, "forking to create", Path); if (Child[0]) {char buff[128]; Parent_Monitor.Start(0,Remote); DEBUG("Waiting for child " <<Child[0] <<" to finish"); retc = bbcp_OS.Waitpid(Child); Parent_Monitor.Stop(); if (bbcp_Config.Options & bbcp_BLAB) write(STDERR_FILENO, buff, Usage("Target", buff, sizeof(buff))); return retc; } /******************************************************************************* (net)->data_link[i]->BPool->CStage[1]->CStage[0]->CPool->outFile->(file) *******************************************************************************/ // Set Concurrency // bbcp_Thread_MT(bbcp_Config.MTLevel); // Request direct I/O if so wanted // if (bbcp_Config.Options & bbcp_ODIO) {fp->FSys()->DirectIO(1); DEBUG("Direct output requested.");} // Open the file and set the starting offset // Elapsed_Timer.Start(); if (!(outFile = fp->FSys()->Open(Path, oflag, Mode, Args))) return bbcp_Emsg("RecvFile", errno, Act, Path); if (startoff && ((retc = outFile->Seek(startoff)) < 0)) return bbcp_Emsg("RecvFile",retc,"setting write offset for",Path); outFile->setSize(fp->Info.size); // If compression is wanted, set up the compression objects // if (bbcp_Config.Options & bbcp_COMPRESS && !(cxp = setup_CX(0, outFile->ioFD()))) return -ECANCELED; // Start a thread for each data link we have // for (i = 0; i < dlcount; i++) {if ((retc = bbcp_Thread_Start(bbcp_Net2Buff, (void *)data_link[i], &tid))<0) {bbcp_Emsg("RecvFile",retc,"starting net thread for",Path); _exit(100); } link_tid[i] = tid; DEBUG("Thread " <<tid <<" assigned to stream " <<i); } // If we are compressing start the sequence thread now // if (bbcp_Config.Options & bbcp_COMPRESS) {seqFile = new bbcp_File(Path, 0, 0); if ((retc = bbcp_Thread_Start(bbcp_doWrite, (void *)seqFile, &tid))<0) {bbcp_Emsg("RecvFile",retc,"starting disk thread for",Path); _exit(100); } link_tid[dlcount++] = tid; DEBUG("Thread " <<tid <<" assigned to sequencer as stream " <<i); } // Start the parent process monitor. It is stopped at exit. // Parent_Monitor.Start(); // If a periodic progress message is wanted, start a progress thread // if (bbcp_Config.Progint) {pmp = new bbcp_ProgMon(); pmp->Start(outFile, cxp, bbcp_Config.Progint, fp->Info.size-startoff); } // Write the whole file // if (bbcp_Config.Options & bbcp_COMPRESS) retc = outFile->Write_All(bbcp_APool, 1); else retc = outFile->Write_All(bbcp_BPool, bbcp_Config.Streams); DEBUG("File write ended; rc=" <<retc); // Wait for the expansion thread to end // if (bbcp_Config.Options & bbcp_COMPRESS) {if (tretc = (long)bbcp_Thread_Wait(cxp->TID)) retc = 128; DEBUG("File expansion ended; rc=" <<tretc); } // Kill the progress monitor // if (pmp) {DEBUG("Deleting progress monitor"); delete pmp; } // Make sure each thread has terminated normally // for (i = 0; i < dlcount; i++) {if (tretc = (long)bbcp_Thread_Wait(link_tid[i])) retc = 128; DEBUG("Thread " <<link_tid[i] <<" stream " <<i <<" ended; rc=" <<tretc); } // Make sure that all of the bytes were transfered // if (!retc && strncmp(Path, "/dev/null/", 10)) {bbcp_FileInfo Info; if ((retc = fp->FSys()->Stat(Path, &Info)) < 0) {retc = -retc; bbcp_Emsg("RecvFile", retc, "finding", Path); } else if (Info.size != fp->Info.size && Info.mode && !(bbcp_Config.Options & bbcp_NOFSZCHK)) {const char *What = (Info.size < fp->Info.size ? "Not all" : "Too much"); retc = 29; bbcp_Fmsg("RecvFile",What,"data was transfered for",Path); DEBUG("src size=" <<fp->Info.size <<" snk size=" <<Info.size); } } DEBUG("Outfile " <<Path <<" closed"); // Report detailed I/O stats, if so wanted // Elapsed_Timer.Stop(); if (!retc && bbcp_Config.Options & bbcp_VERBOSE) {double ttime; Elapsed_Timer.Report(ttime); Report(ttime, fp, outFile, cxp); } // All done // Parent_Monitor.Stop(); delete outFile; if (cxp) delete(cxp); if (seqFile) delete(seqFile); retc = fp->Finalize(retc); close(1); close(2); DEBUG("Process " <<getpid() <<" exiting with rc=" <<retc); exit(retc); return(retc); // some compilers insist on a return in int functions }
int bbcp_Node::SendFile(bbcp_FileSpec *fp) { const char *Act = "opening"; int i, retc, tretc = 0, Mode = 0; pid_t Child[2] = {0,0}; bbcp_File *inFile; bbcp_ProcMon *TLimit = 0; bbcp_ZCX *cxp; pthread_t tid, link_tid[BBCP_MAXSTREAMS+1]; // Set open options (check for pipes) // if (bbcp_Config.Options & bbcp_XPIPE) {if (fp->Info.Otype == 'p') Mode = S_IFIFO; else Act = "running"; } // Send the file in a sub-process so that we don't muck with this one // DEBUG("Sending file " <<fp->targpath <<"; offset=" <<fp->targetsz); if ((Child[0] = bbcp_OS.Fork()) < 0) return bbcp_Emsg("SendFile", errno, "forking to send", fp->pathname); if (Child[0]) {char buff[128]; Parent_Monitor.Start(); retc = bbcp_OS.Waitpid(Child); Parent_Monitor.Stop(); if (bbcp_Config.Options & bbcp_BLAB) write(STDERR_FILENO, buff, Usage("Source", buff, sizeof(buff))); return retc; } /******************************************************************************* (file)->inFile->CPool->CStage[0]->CStage[1]->BPool->data_link[i]->(net) *******************************************************************************/ // Set Concurrency // bbcp_Thread_MT(bbcp_Config.MTLevel); // Request direct I/O if so wanted // if (bbcp_Config.Options & bbcp_IDIO) {fp->FSys()->DirectIO(1); DEBUG("Direct input requested.");} // Open the input file and set starting offset // if (!(inFile = fp->FSys()->Open(fp->pathname,O_RDONLY,Mode,fp->fileargs))) {bbcp_Emsg("SendFile", errno, Act, fp->pathname); exit(2); } if (fp->targetsz && ((retc = inFile->Seek(fp->targetsz)) < 0)) return bbcp_Emsg("SendFile",retc,"setting read offset for",fp->pathname); // If compression is wanted, set up the compression objects // if (bbcp_Config.Options & bbcp_COMPRESS && !(cxp = setup_CX(1, inFile->ioFD()))) return -ECANCELED; // Start a thread for each data link we have // for (i = 0; i < dlcount; i++) {if ((retc = bbcp_Thread_Start(bbcp_Buff2Net, (void *)data_link[i], &tid))<0) {bbcp_Emsg("SendFile",retc,"starting net thread for",fp->pathname); _exit(100); } link_tid[i] = tid; if (i >= iocount) {DEBUG("Thread " <<tid <<" assigned to data clocker");} else {DEBUG("Thread " <<tid <<" assigned to stream " <<i);} } // Start the parent monitor // Parent_Monitor.Start(); // Start the Transfer Time Limit // if (bbcp_Config.TimeLimit) {TLimit = new bbcp_ProcMon(); TLimit->Start(bbcp_Config.TimeLimit, &bbcp_BPool); } // Read the whole file // if (bbcp_Config.Options & bbcp_COMPRESS) retc=inFile->Read_All(bbcp_APool,1); else retc = inFile->Read_All(bbcp_BPool, bbcp_Config.Bfact); DEBUG("File read ended; rc=" <<retc); // Wait for compression thread to end // if (bbcp_Config.Options & bbcp_COMPRESS) {if (tretc = (long)bbcp_Thread_Wait(cxp->TID)) retc = 128; DEBUG("File compression ended; rc=" <<tretc); delete cxp; } // Make sure each link thread has terminated normally. // for (i = 0; i < iocount; i++) {if (tretc = (long)bbcp_Thread_Wait(link_tid[i])) retc = 128; DEBUG("Thread " <<link_tid[i] <<" stream " <<i <<" ended; rc=" <<tretc); } // All done // if (TLimit) delete TLimit; Parent_Monitor.Stop(); delete inFile; close(1); close(2); DEBUG("Process " <<getpid() <<" exiting with rc=" <<retc); exit(retc); return(retc); // some compilers insist on a return in int functions }
// The following class is here to allow graceful error exits // int bbcp_Protocol::Process_login(bbcp_Link *Net) { char buff[256], *tp, *bp, *vp, *wp, *id; int retc, blen, respWS; bbcp_Login_Stream loginStream(Net); bbcp_Node *np = loginStream.np; // Get the first line of the login stream // if (!(np->GetLine())) {if (retc = np->LastError()) return bbcp_Emsg("Process_Login", retc, "processing login from", Net->LinkName()); return bbcp_Fmsg("Process_Login", "Bad login from", Net->LinkName()); } // Determine the id we want (the control stream must login first) // id = (Remote ? (char *)"data" : (char *)"ctlr"); // Process the login request: login <id> <password> // if (!(wp = np->GetToken()) || strcmp(wp, "login") || !(wp = np->GetToken()) || strcmp(wp, id) || !(wp = np->GetToken()) || strcmp(wp, bbcp_Config.SecToken)) return bbcp_Fmsg("Process_Login", "Invalid login from", Net->LinkName()); // We are all done if this is not a control stream // if (*id != 'c') {np->Detach(); return 0;} // Pickup all parameters. // bp = vp = wp = 0; while((tp = np->GetToken())) { if (!strcmp(tp, "wsz:")) {if (!(wp = np->GetToken())) return bbcp_Fmsg("Login", "Window size is missing."); } else if (!strcmp(tp, "ver:")) {if (!(vp = np->GetToken())) return bbcp_Fmsg("Login", "Version is missing."); } else if (!strcmp(tp, "bsz:")) {if (!(bp = np->GetToken())) return bbcp_Fmsg("Login", "Buffer size is missing."); } } // Verify that our version is the same on the other side // if (vp) bbcp_Version.Verify(Net->LinkName(), vp); else bbcp_Version.Verify(Net->LinkName(),(char *)"02.01.12.00.0"); // We can now do a window/buffer adjustment // if (!wp) respWS = bbcp_Config.Wsize; else if (!(respWS = AdjustWS(wp, bp, 0))) return -1; // Respond to this login request (control only gets a response) // blen = sprintf(buff, "204 loginok wsz: %d %d\n",respWS,bbcp_Config.RWBsz); if ((retc = np->Put(buff, blen)) < 0) return -1; // All done // Remote = np; loginStream.np = 0; return 1; }
int bbcp_File::Passthru(bbcp_BuffPool *iBP, bbcp_BuffPool *oBP, bbcp_FileChkSum *csP, int nstrms) { bbcp_Buffer *outbuff; bbcp_ChkSum *csObj; long long Offset = nextoffset; int csLen, csVer, numadd, maxbufs, maxadds = nstrms; int rc = 0, unordered = !(bbcp_Config.Options & bbcp_ORDER); // Determine if we will be piggy-backing checksumming here // if (csP && (csObj = csP->csObj)) {csVer = csP->csVer; csLen = csObj->csSize();} else csVer = csLen = 0; // Record the maximum number of buffers we have here // maxbufs = iBP->BuffCount(); if (!(numadd = nstrms)) numadd = 1; // Read all of the data until eof (note that we are single threaded) // while(nstrms) { // Obtain a full buffer // if (!(outbuff = iBP->getFullBuff())) break; // Check if this is an eof marker // // cerr <<nstrms <<" Passt " <<outbuff->blen <<'@' <<outbuff->boff <<endl; if (!(outbuff->blen)) {iBP->putEmptyBuff(outbuff); nstrms--; continue;} // Do an unordered write if allowed // if (unordered) {oBP->putFullBuff(outbuff); continue;} // Check if this buffer is in the correct sequence // if (outbuff->boff != Offset) {if (outbuff->boff < 0) {rc = -ESPIPE; break;} outbuff->next = nextbuff; nextbuff = outbuff; bufreorders++; if (++curq > maxreorders) {maxreorders = curq; DEBUG("Buff disorder " <<curq <<" rcvd " <<outbuff->boff <<" want " <<Offset); } if (curq >= maxbufs) {if (!(--maxadds)) {rc = -ENOBUFS; break;} DEBUG("Too few buffs; adding " <<numadd <<" more."); bbcp_BPool.Allocate(numadd); maxbufs += numadd; } continue; } // Pass through any queued buffers // do {Offset += outbuff->blen; if (csObj) {csObj->Update(outbuff->data, outbuff->blen); if (csVer && memcmp(outbuff->bHdr.cksm, csObj->csCurr(), csLen)) {char buff[32]; sprintf(buff, "%lld", outbuff->boff); bbcp_Fmsg("Write",iofn, "xfr checksum error at offset",buff); rc = -EILSEQ; nstrms = 0; break; } } oBP->putFullBuff(outbuff); } while(nextbuff && (outbuff = getBuffer(Offset))); } // Check if we should print an error here // if (rc && rc != -EILSEQ) bbcp_Emsg("Write",rc, "unable to write", iofn); // Queue an empty buffer indicating eof or abort the stream // if (!rc && (outbuff = iBP->getEmptyBuff())) {outbuff->blen = 0; outbuff->boff = Offset; oBP->putFullBuff(outbuff); } else { if (!rc) rc = -ENOBUFS; oBP->Abort(); iBP->Abort(); } // All done // return rc; }
int bbcp_File::Read_All(bbcp_BuffPool &inPool, int Vn) { bbcp_FileChkSum *csP = 0; bbcp_BuffPool *outPool; bbcp_Buffer *bP; pthread_t tid; int rc = 0; // Get the size of the file // if ((bytesLeft = FSp->getSize(IOB->FD())) < 0) {bbcp_Emsg("Read", static_cast<int>(-bytesLeft), "stat", iofn); inPool.Abort(); return 200; } // Adjust bytes left based on where we will be reading from // bytesLeft -= nextoffset; if (bytesLeft < 0) {bbcp_Emsg("Read", ESPIPE, "stat", iofn); inPool.Abort(); return 200; } // If this is a real-time copy operation, start the rtcopy object // if (rtCopy && !bbcp_RTCopy.Start(FSp, iofn, IOB->FD())) {inPool.Abort(); return 200; } // Set up checksumming. We would prefer to do this in the calling thread but // this is easier. One day we will generalize buffer piping. // if (bbcp_Config.csOpts & bbcp_csVerIn) {csP = new bbcp_FileChkSum(&inPool, this, bbcp_Config.csType, bbcp_Config.csOpts & bbcp_csVerIO); if ((rc = bbcp_Thread_Start(bbcp_FileCSX, (void *)csP, &tid)) < 0) {bbcp_Emsg("File", rc, "starting file checksum thread."); delete csP; if (rtCopy) bbcp_RTCopy.Stop(); inPool.Abort(); return 201; } outPool = &csP->csPool; } else outPool = &inPool; // Establish logging options // if (bbcp_Config.Options & bbcp_LOGRD) IOB->Log("DISK", 0); // Determine what kind of reading we will do here and do it // // cerr <<"BLOCKSIZE " <<blockSize <<endl; if (blockSize ) rc = Read_Direct(&inPool, outPool); else rc=(Vn > 1 ? Read_Vector(&inPool, outPool, Vn) : Read_Normal(&inPool, outPool)); // Delete the real-time copy object if we have one to kill possible thread // if (rtCopy) bbcp_RTCopy.Stop(); // Check if we ended because with an error // IOB->Close(); if (rc && rc != -ENOBUFS) bbcp_Emsg("Read", -rc, "reading", iofn); // Prepare an empty buffer to shutdown the buffer pipeline. The offet indicates // how much data should have been sent and received. A negative offset implies // that we encountered an error. // if (!(bP = inPool.getEmptyBuff())) return 255; bP->blen = 0; bP->boff = (rc ? -1 : nextoffset); // Verify check sum if so wanted. Note that is link-level check summing is in // if (csP) {csP->csPool.putFullBuff(bP); bbcp_Thread_Wait(tid); if (!rc && *bbcp_Config.csString) {char *csTxt, *csVal = csP->csObj->Final(&csTxt); if (memcmp(csVal, bbcp_Config.csValue, csP->csObj->csSize())) {bbcp_Fmsg("Read", iofn, "source checksum", bbcp_Config.csString, "does not match", csTxt); rc = EILSEQ; } else {DEBUG(csP->csObj->Type() <<": " <<csTxt <<' ' <<iofn);} } delete csP; } else inPool.putFullBuff(bP); // All done // DEBUG("EOF offset=" <<nextoffset <<" rc=" <<rc <<" fn=" <<iofn); return rc; }