📄 io.c
字号:
/* Nice for UNIX, but not necessary otherwise. */
#ifdef TAR
static int
OpenTar(const FTPCIPtr cip, const char *const dstdir, int *const pid)
{
int pipe1[2];
int pid1;
int i;
char *argv[8];
*pid = -1;
if (access(TAR, X_OK) < 0) {
/* Path to TAR is invalid. */
return (-1);
}
if (pipe(pipe1) < 0) {
Error(cip, kDoPerror, "pipe to Tar failed");
return (-1);
}
pid1 = (int) fork();
if (pid1 < 0) {
(void) close(pipe1[0]);
(void) close(pipe1[1]);
return (-1);
} else if (pid1 == 0) {
/* Child */
if ((dstdir != NULL) && (dstdir[0] != '\0') && (chdir(dstdir) < 0)) {
Error(cip, kDoPerror, "tar chdir to %s failed", dstdir);
exit(1);
}
(void) close(pipe1[1]); /* close write end */
(void) dup2(pipe1[0], 0); /* use read end on stdin */
(void) close(pipe1[0]);
for (i=3; i<256; i++)
(void) close(i);
argv[0] = (char *) "tar";
argv[1] = (char *) "xpf";
argv[2] = (char *) "-";
argv[3] = NULL;
(void) execv(TAR, argv);
exit(1);
}
/* Parent */
*pid = pid1;
(void) close(pipe1[0]); /* close read end */
return (pipe1[1]); /* use write end */
} /* OpenTar */
static int
FTPGetOneTarF(const FTPCIPtr cip, const char *file, const char *const dstdir)
{
char *buf;
size_t bufSize;
int tmpResult;
volatile int result;
int nread, nwrote;
volatile int fd;
volatile int vfd;
const char *volatile vfile;
#ifndef NO_SIGNALS
int sj;
volatile FTPSigProc osigpipe;
volatile FTPCIPtr vcip;
#endif
int pid, status;
char savedCwd[512];
char *volatile basecp;
result = kNoErr;
cip->usingTAR = 0;
if ((file[0] == '\0') || ((file[0] == '/') && (file[1] == '\0'))) {
/* It was "/"
* We can't do that, because "get /.tar"
* or "get .tar" does not work.
*/
result = kErrOpenFailed;
cip->errNo = kErrOpenFailed;
return (result);
}
if (FTPCmd(cip, "MDTM %s.tar", file) == 2) {
/* Better not use this method since there is
* no way to tell if the server would use the
* existing .tar or do a new one on the fly.
*/
result = kErrOpenFailed;
cip->errNo = kErrOpenFailed;
return (result);
}
basecp = strrchr(file, '/');
if (basecp != NULL)
basecp = strrchr(file, '\\');
if (basecp != NULL) {
/* Need to cd to the parent directory and get it
* from there.
*/
if (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != 0) {
result = kErrOpenFailed;
cip->errNo = kErrOpenFailed;
return (result);
}
result = FTPChdir(cip, file);
if (result != kNoErr) {
return (result);
}
result = FTPChdir(cip, "..");
if (result != kNoErr) {
(void) FTPChdir(cip, savedCwd);
return (result);
}
file = basecp + 1;
}
fd = OpenTar(cip, dstdir, &pid);
if (fd < 0) {
result = kErrOpenFailed;
cip->errNo = kErrOpenFailed;
if (basecp != NULL)
(void) FTPChdir(cip, savedCwd);
return (result);
}
vfd = fd;
vfile = file;
#ifndef NO_SIGNALS
vcip = cip;
osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
gGotBrokenData = 0;
gCanBrokenDataJmp = 0;
#ifdef HAVE_SIGSETJMP
sj = sigsetjmp(gBrokenDataJmp, 1);
#else
sj = setjmp(gBrokenDataJmp);
#endif /* HAVE_SIGSETJMP */
if (sj != 0) {
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
FTPShutdownHost(vcip);
(void) signal(SIGPIPE, SIG_IGN);
(void) close(vfd);
for (;;) {
#ifdef HAVE_WAITPID
if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
break;
#else
if ((wait(&status) < 0) && (errno != EINTR))
break;
#endif
if (WIFEXITED(status) || WIFSIGNALED(status))
break; /* done */
}
if (basecp != NULL)
(void) FTPChdir(cip, savedCwd);
vcip->errNo = kErrRemoteHostClosedConnection;
return(vcip->errNo);
}
gCanBrokenDataJmp = 1;
#endif /* NO_SIGNALS */
tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, (longest_int) 0, "RETR %s.tar", vfile);
if (tmpResult < 0) {
result = tmpResult;
if (result == kErrGeneric)
result = kErrRETRFailed;
cip->errNo = result;
#ifndef NO_SIGNALS
(void) signal(SIGPIPE, SIG_IGN);
#endif
(void) close(vfd);
for (;;) {
#ifdef HAVE_WAITPID
if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
break;
#else
if ((wait(&status) < 0) && (errno != EINTR))
break;
#endif
if (WIFEXITED(status) || WIFSIGNALED(status))
break; /* done */
}
#ifndef NO_SIGNALS
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
#endif
if (basecp != NULL)
(void) FTPChdir(cip, savedCwd);
return (result);
}
cip->usingTAR = 1;
buf = cip->buf;
bufSize = cip->bufSize;
FTPInitIOTimer(cip);
cip->lname = vfile; /* could be NULL */
cip->rname = vfile;
FTPStartIOTimer(cip);
/* Binary */
for (;;) {
if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */
cip->errNo = result = kErrDataTimedOut;
Error(cip, kDontPerror, "Remote read timed out.\n");
break;
}
if (cip->cancelXfer > 0) {
FTPAbortDataTransfer(cip);
result = cip->errNo = kErrDataTransferAborted;
break;
}
#if !defined(NO_SIGNALS)
gCanBrokenDataJmp = 1;
if (cip->xferTimeout > 0)
(void) alarm(cip->xferTimeout);
#endif /* NO_SIGNALS */
#ifdef NO_SIGNALS
nread = SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
if (nread == kTimeoutErr) {
cip->errNo = result = kErrDataTimedOut;
Error(cip, kDontPerror, "Remote read timed out.\n");
break;
} else if (nread < 0) {
if (errno == EINTR)
continue;
Error(cip, kDoPerror, "Remote read failed.\n");
result = kErrSocketReadFailed;
cip->errNo = kErrSocketReadFailed;
break;
} else if (nread == 0) {
break;
}
#else
nread = read(cip->dataSocket, buf, bufSize);
if (nread < 0) {
if (errno == EINTR)
continue;
Error(cip, kDoPerror, "Remote read failed.\n");
result = kErrSocketReadFailed;
cip->errNo = kErrSocketReadFailed;
break;
} else if (nread == 0) {
break;
}
gCanBrokenDataJmp = 0;
#endif
nwrote = write(fd, buf, nread);
if (nwrote != nread) {
if ((gGotBrokenData != 0) || (errno == EPIPE)) {
result = kErrWriteFailed;
cip->errNo = kErrWriteFailed;
errno = EPIPE;
} else {
Error(cip, kDoPerror, "Local write failed.\n");
result = kErrWriteFailed;
cip->errNo = kErrWriteFailed;
}
break;
}
cip->bytesTransferred += (longest_int) nread;
FTPUpdateIOTimer(cip);
}
#if !defined(NO_SIGNALS)
if (cip->xferTimeout > 0)
(void) alarm(0);
gCanBrokenDataJmp = 0;
#endif /* NO_SIGNALS */
(void) close(fd);
for (;;) {
#ifdef HAVE_WAITPID
if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
break;
#else
if ((wait(&status) < 0) && (errno != EINTR))
break;
#endif
if (WIFEXITED(status) || WIFSIGNALED(status))
break; /* done */
}
tmpResult = FTPEndDataCmd(cip, 1);
if ((tmpResult < 0) && (result == 0)) {
result = kErrRETRFailed;
cip->errNo = kErrRETRFailed;
}
FTPStopIOTimer(cip);
#if !defined(NO_SIGNALS)
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
#endif
if ((result == 0) && (cip->bytesTransferred == 0)) {
result = kErrOpenFailed;
cip->errNo = kErrOpenFailed;
}
if (basecp != NULL)
(void) FTPChdir(cip, savedCwd);
return (result);
} /* FTPGetOneTarF */
#endif /* TAR */
static int
FTPGetOneF(
const FTPCIPtr cip,
const char *const file,
const char *dstfile,
int xtype,
const int fdtouse,
longest_int expectedSize,
time_t mdtm,
const int resumeflag,
const int appendflag,
const int deleteflag,
const ConfirmResumeDownloadProc resumeProc)
{
char *buf;
size_t bufSize;
int tmpResult;
volatile int result;
int nread, nwrote;
volatile int fd;
#if ASCII_TRANSLATION
char *src, *srclim;
char *dst, *dstlim;
char outbuf[512];
#endif
volatile longest_int startPoint = 0;
struct utimbuf ut;
struct Stat st;
#if !defined(NO_SIGNALS)
volatile FTPSigProc osigpipe;
volatile FTPCIPtr vcip;
volatile int vfd, vfdtouse;
int sj;
#endif /* NO_SIGNALS */
volatile int created = 0;
int zaction = kConfirmResumeProcSaidBestGuess;
int statrc;
int noMdtmCheck;
time_t now;
if (cip->buf == NULL) {
Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
cip->errNo = kErrNoBuf;
return (cip->errNo);
}
result = kNoErr;
cip->usingTAR = 0;
if (fdtouse < 0) {
/* Only ask for extended information
* if we have the name of the file
* and we didn't already have the
* info.
*
* Always ask for the modification time,
* because even if it was passed in it
* may not be accurate. This is often
* the case when it came from an ls
* listing, in which the local time
* zone could be a factor.
*
*/
AutomaticallyUseASCIIModeDependingOnExtension(cip, file, &xtype);
if (expectedSize == kSizeUnknown) {
(void) FTPFileSizeAndModificationTime(cip, file, &expectedSize, xtype, &mdtm);
} else {
(void) FTPFileModificationTime(cip, file, &mdtm);
}
/* For Get, we can't recover very well if it turns out restart
* didn't work, so check beforehand.
*/
if ((resumeflag == kResumeYes) || (resumeProc != NoConfirmResumeDownloadProc)) {
if (cip->hasREST == kCommandAvailabilityUnknown) {
(void) FTPSetTransferType(cip, kTypeBinary);
if (SetStartOffset(cip, (longest_int) 1) == kNoErr) {
/* Now revert -- we still may not end up
* doing it.
*/
SetStartOffset(cip, (longest_int) -1);
}
}
}
if (appendflag == kAppendYes) {
zaction = kConfirmResumeProcSaidAppend;
} else if (cip->hasREST == kCommandNotAvailable) {
zaction = kConfirmResumeProcSaidOverwrite;
} else if (resumeflag == kResumeYes) {
zaction = kConfirmResumeProcSaidBestGuess;
} else {
zaction = kConfirmResumeProcSaidOverwrite;
}
statrc = Stat(dstfile, &st);
if (statrc == 0) {
if (resumeProc != NULL) {
zaction = (*resumeProc)(
&dstfile,
(longest_int) st.st_size,
st.st_mtime,
file,
expectedSize,
mdtm,
&startPoint
);
}
if (zaction == kConfirmResumeProcSaidBestGuess) {
if (expectedSize != kSizeUnknown) {
/* We know the size of the remote file,
* and we have a local file too.
*
* Try and decide if we need to get
* the entire file, or just part of it.
*/
startPoint = (longest_int) st.st_size;
zaction = kConfirmResumeProcSaidResume;
/* If the local file exists and has a recent
* modification time (< 12 hours) and
* the remote file's modtime is not recent,
* then heuristically conclude that the
* local modtime should not be trusted
* (i.e. user killed the process before
* the local modtime could be preserved).
*/
noMdtmCheck = 0;
if (mdtm != kModTimeUnknown) {
time(&now);
if ((st.st_mtime > now) || (((now - st.st_mtime) < 46200) && ((now - mdtm) >= 46200)))
noMdtmCheck = 1;
}
if ((mdtm == kModTimeUnknown) || (noMdtmCheck != 0)) {
/* Can't use the timestamps as an aid. */
if (startPoint == expectedSize) {
/* Don't go to all the trouble of downloading nothing. */
cip->errNo = kErrLocalSameAsRemote;
if (deleteflag == kDeleteYes)
(void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
return (cip->errNo);
} else if (startPoint > expectedSize) {
/* Panic; odds are the file we have
* was a different file altogether,
* since it is larger than the
* remote copy. Re-do it all.
*/
zaction = kConfirmResumeProcSaidOverwrite;
} /* else resume at startPoint */
} else if ((mdtm == st.st_mtime) || (mdtm == (st.st_mtime - 1)) || (mdtm == (st.st_mtime + 1))) {
/* File has the same time.
* Note: Windows' file timestamps can be off by one second!
*/
if (startPoint == expectedSize) {
/* Don't go to all the trouble of downloading nothing. */
cip->errNo = kErrLocalSameAsRemote;
if (deleteflag == kDeleteYes)
(void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
return (cip->errNo);
} else if (startPoint > expectedSize) {
/* Panic; odds are the file we have
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -