📄 io.c
字号:
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);
vcip->errNo = kErrRemoteHostClosedConnection;
return(vcip->errNo);
}
gCanBrokenDataJmp = 1;
if (result == 0) {
/* This line sets the buffer pointer so that the first thing
* BufferGets will do is reset and fill the buffer using
* real I/O.
*/
secBufPtr = secondaryBuf + sizeof(secondaryBuf);
secBufLimit = (char *) 0;
memset(secondaryBuf, 0, sizeof(secondaryBuf));
for (;;) {
memset(line, 0, sizeof(line));
if (cip->xferTimeout > 0)
(void) alarm(cip->xferTimeout);
nread = BufferGets(line, sizeof(line), cip->dataSocket, secondaryBuf, &secBufPtr, &secBufLimit, sizeof(secondaryBuf));
if (nread <= 0) {
if (nread < 0)
break;
if (blankLines != 0)
(void) AddLine(llines, line);
} else {
cip->bytesTransferred += (longest_int) nread;
if ((line[0] == '.') && ((line[1] == '\0') || ((line[1] == '.') && ((line[2] == '\0') || (iscntrl(line[2]))))))
continue; /* Skip . and .. */
(void) AddLine(llines, line);
}
}
if (cip->xferTimeout > 0)
(void) alarm(0);
result = FTPEndDataCmd(cip, 1);
if (result < 0) {
result = kErrLISTFailed;
cip->errNo = kErrLISTFailed;
}
result = kNoErr;
cip->numListings++;
} else if (result == kErrGeneric) {
result = kErrLISTFailed;
cip->errNo = kErrLISTFailed;
}
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
#endif /* NO_SIGNALS */
return (result);
} /* FTPListToMemory2 */
static void
AutomaticallyUseASCIIModeDependingOnExtension(const FTPCIPtr cip, const char *const pathName, int *const xtype)
{
if ((*xtype == kTypeBinary) && (cip->asciiFilenameExtensions != NULL)) {
if (FilenameExtensionIndicatesASCII(pathName, cip->asciiFilenameExtensions)) {
/* Matched -- send this file in ASCII mode
* instead of binary since it's extension
* appears to be that of a text file.
*/
*xtype = kTypeAscii;
}
}
} /* AutomaticallyUseASCIIModeDependingOnExtension */
/* The purpose of this is to provide updates for the progress meters
* during lags. Return zero if the operation timed-out.
*/
static int
WaitForRemoteOutput(const FTPCIPtr cip)
{
fd_set ss, ss2;
struct timeval tv;
int result;
int fd;
int wsecs;
int xferTimeout;
int ocancelXfer;
xferTimeout = cip->xferTimeout;
if (xferTimeout < 1)
return (1);
fd = cip->dataSocket;
if (fd < 0)
return (1);
ocancelXfer = cip->cancelXfer;
wsecs = 0;
cip->stalled = 0;
while ((xferTimeout <= 0) || (wsecs < xferTimeout)) {
if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) {
/* leave cip->stalled -- could have been stalled and then canceled. */
return (1);
}
FD_ZERO(&ss);
FD_SET(fd, &ss);
ss2 = ss;
tv.tv_sec = 1;
tv.tv_usec = 0;
result = select(fd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &ss2, &tv);
if (result == 1) {
/* ready */
cip->stalled = 0;
return (1);
} else if (result < 0) {
if (errno != EINTR) {
perror("select");
cip->stalled = 0;
return (1);
}
} else {
wsecs++;
cip->stalled = wsecs;
}
FTPUpdateIOTimer(cip);
}
#if !defined(NO_SIGNALS)
/* Shouldn't get here -- alarm() should have
* went off by now.
*/
(void) kill(getpid(), SIGALRM);
#endif /* NO_SIGNALS */
cip->dataTimedOut = 1;
return (0); /* timed-out */
} /* WaitForRemoteOutput */
static int
FTPPutOneF(
const FTPCIPtr cip,
const char *const file,
const char *volatile dstfile,
int xtype,
const int fdtouse,
const int appendflag,
const char *volatile tmppfx,
const char *volatile tmpsfx,
const int resumeflag,
const int deleteflag,
const ConfirmResumeUploadProc resumeProc)
{
char *buf, *cp;
const char *cmd;
const char *odstfile;
size_t bufSize;
size_t l;
int tmpResult, result;
int nread, nwrote;
volatile int fd;
char dstfile2[512];
#if ASCII_TRANSLATION
char *src, *srclim, *dst;
int ntowrite;
char inbuf[256];
#endif
int fstatrc, statrc;
longest_int startPoint = 0;
struct Stat st;
time_t mdtm;
#if !defined(NO_SIGNALS)
int sj;
volatile FTPSigProc osigpipe;
volatile FTPCIPtr vcip;
volatile int vfd, vfdtouse;
#endif /* NO_SIGNALS */
volatile int vzaction;
int zaction = kConfirmResumeProcSaidBestGuess;
if (cip->buf == NULL) {
Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
cip->errNo = kErrNoBuf;
return (cip->errNo);
}
cip->usingTAR = 0;
if (fdtouse < 0) {
fd = Open(file, O_RDONLY|O_BINARY, 0);
if (fd < 0) {
Error(cip, kDoPerror, "Cannot open local file %s for reading.\n", file);
cip->errNo = kErrOpenFailed;
return (cip->errNo);
}
} else {
fd = fdtouse;
}
fstatrc = Fstat(fd, &st);
if ((fstatrc == 0) && (S_ISDIR(st.st_mode))) {
if (fdtouse < 0) {
(void) close(fd);
}
Error(cip, kDontPerror, "%s is a directory.\n", (file != NULL) ? file : "that");
cip->errNo = kErrOpenFailed;
return (cip->errNo);
}
/* For Put, we can't recover very well if it turns out restart
* didn't work, so check beforehand.
*/
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 (fdtouse < 0) {
AutomaticallyUseASCIIModeDependingOnExtension(cip, dstfile, &xtype);
(void) FTPFileSizeAndModificationTime(cip, dstfile, &startPoint, xtype, &mdtm);
if (appendflag == kAppendYes) {
zaction = kConfirmResumeProcSaidAppend;
} else if (
(cip->hasREST == kCommandNotAvailable) ||
(xtype != kTypeBinary) ||
(fstatrc < 0)
) {
zaction = kConfirmResumeProcSaidOverwrite;
} else if (resumeflag == kResumeYes) {
zaction = kConfirmResumeProcSaidBestGuess;
} else {
zaction = kConfirmResumeProcSaidOverwrite;
}
statrc = -1;
if ((mdtm != kModTimeUnknown) || (startPoint != kSizeUnknown)) {
/* Then we know the file exists. We will
* ask the user what to do, if possible, below.
*/
statrc = 0;
} else if ((resumeProc != NoConfirmResumeUploadProc) && (cip->hasMDTM != kCommandAvailable) && (cip->hasSIZE != kCommandAvailable)) {
/* We already checked if the file had a filesize
* or timestamp above, but if the server indicated
* it did not support querying those directly,
* we now need to try to determine if the file
* exists in a few other ways.
*/
statrc = FTPFileExists2(cip, dstfile, 0, 0, 0, 1, 1);
}
if (
(resumeProc != NoConfirmResumeUploadProc) &&
(statrc == 0)
) {
zaction = (*resumeProc)(file, (longest_int) st.st_size, st.st_mtime, &dstfile, startPoint, mdtm, &startPoint);
}
if (zaction == kConfirmResumeProcSaidCancel) {
/* User wants to cancel this file and any
* remaining in batch.
*/
cip->errNo = kErrUserCanceled;
return (cip->errNo);
}
if (zaction == kConfirmResumeProcSaidBestGuess) {
if ((mdtm != kModTimeUnknown) && (st.st_mtime > (mdtm + 1))) {
/* Local file is newer than remote,
* overwrite the remote file instead
* of trying to resume it.
*
* Note: Add one second fudge factor
* for Windows' file timestamps being
* imprecise to one second.
*/
zaction = kConfirmResumeProcSaidOverwrite;
} else if ((longest_int) st.st_size == startPoint) {
/* Already sent file, done. */
zaction = kConfirmResumeProcSaidSkip;
} else if ((startPoint != kSizeUnknown) && ((longest_int) st.st_size > startPoint)) {
zaction = kConfirmResumeProcSaidResume;
} else {
zaction = kConfirmResumeProcSaidOverwrite;
}
}
if (zaction == kConfirmResumeProcSaidSkip) {
/* Nothing done, but not an error. */
if (fdtouse < 0) {
(void) close(fd);
}
if (deleteflag == kDeleteYes) {
if (unlink(file) < 0) {
cip->errNo = kErrLocalDeleteFailed;
return (cip->errNo);
}
}
return (kNoErr);
} else if (zaction == kConfirmResumeProcSaidResume) {
/* Resume; proc set the startPoint. */
if ((longest_int) st.st_size == startPoint) {
/* Already sent file, done. */
if (fdtouse < 0) {
(void) close(fd);
}
if (deleteflag == kDeleteYes) {
if (unlink(file) < 0) {
cip->errNo = kErrLocalDeleteFailed;
return (cip->errNo);
}
}
return (kNoErr);
} else if (Lseek(fd, (off_t) startPoint, SEEK_SET) != (off_t) -1) {
cip->startPoint = startPoint;
}
} else if (zaction == kConfirmResumeProcSaidAppend) {
/* append: leave startPoint at zero, we will append everything. */
cip->startPoint = startPoint = 0;
} else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
/* overwrite: leave startPoint at zero */
cip->startPoint = startPoint = 0;
}
}
if ((cip->numUploads == 0) && (cip->dataSocketSBufSize > 0)) {
/* If dataSocketSBufSize is non-zero, it means you
* want to explicitly try to set the size of the
* socket's I/O buffer.
*
* If it is zero, it means you want to just use the
* TCP stack's default value, which is typically
* between 8 and 64 kB.
*
* If you try to set the buffer larger than 64 kB,
* the TCP stack should try to use RFC 1323 to
* negotiate "TCP Large Windows" which may yield
* significant performance gains.
*/
if (cip->hasSTORBUFSIZE == kCommandAvailable)
(void) FTPCmd(cip, "SITE STORBUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
else if (cip->hasSBUFSIZ == kCommandAvailable)
(void) FTPCmd(cip, "SITE SBUFSIZ %lu", (unsigned long) cip->dataSocketSBufSize);
else if (cip->hasSBUFSZ == kCommandAvailable)
(void) FTPCmd(cip, "SITE SBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize);
/* At least one server implemenation has RBUFSZ but not
* SBUFSZ and instead uses RBUFSZ for both.
*/
else if ((cip->hasSBUFSZ != kCommandAvailable) && (cip->hasRBUFSZ == kCommandAvailable))
(void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize);
else if (cip->hasBUFSIZE == kCommandAvailable)
(void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
}
#ifdef NO_SIGNALS
vzaction = zaction;
#else /* NO_SIGNALS */
vcip = cip;
vfdtouse = fdtouse;
vfd = fd;
vzaction = zaction;
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);
if (vfdtouse < 0) {
(void) close(vfd);
}
FTPShutdownHost(vcip);
vcip->errNo = kErrRemoteHostClosedConnection;
return(vcip->errNo);
}
gCanBrokenDataJmp = 1;
#endif /* NO_SIGNALS */
if (vzaction == kConfirmResumeProcSaidAppend) {
cmd = "APPE";
tmppfx = ""; /* Can't use that here. */
tmpsfx = "";
} else {
cmd = "STOR";
if (tmppfx == NULL)
tmppfx = "";
if (tmpsfx == NULL)
tmpsfx = "";
}
odstfile = dstfile;
if ((tmppfx[0] != '\0') || (tmpsfx[0] != '\0')) {
cp = strrchr(dstfile, '/');
if (cp == NULL)
cp = strrchr(dstfile, '\\');
if (cp == NULL) {
(void) STRNCPY(dstfile2, tmppfx);
(void) STRNCAT(dstfile2, dstfile);
(void) STRNCAT(dstfile2, tmpsfx);
} else {
cp++;
l = (size_t) (cp - dstfile);
(void) STRNCPY(dstfile2, dstfile);
dstfile2[l] = '\0'; /* Nuke stuff after / */
(void) STRNCAT(dstfile2, tmppfx);
(void) STRNCAT(dstfile2, cp);
(void) STRNCAT(dstfile2, tmpsfx);
}
dstfile = dstfile2;
}
tmpResult = FTPStartDataCmd(
cip,
kNetWriting,
xtype,
startPoint,
"%s %s",
cmd,
dstfile
);
if (tmpResult < 0) {
cip->errNo = tmpResult;
if (fdtouse < 0) {
(void) close(fd);
}
#if !defined(NO_SIGNALS)
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
#endif /* NO_SIGNALS */
return (cip->errNo);
}
if ((startPoint != 0) && (cip->startPoint == 0)) {
/* Remote could not or would not set the start offset
* to what we wanted.
*
* So now we have to undo our seek.
*/
if (Lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
cip->errNo = kErrLseekFailed;
if (fdtouse < 0) {
(void) close(fd);
}
#if !defined(NO_SIGNALS)
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
#endif /* NO_SIGNALS */
return (cip->errNo);
}
startPoint = 0;
}
result = kNoErr;
buf = cip->buf;
bufSize = cip->bufSize;
FTPInitIOTimer(cip);
if ((fstatrc == 0) && (S_ISREG(st.st_mode) != 0)) {
cip->expectedSize = (longest_int) st.st_size;
cip->mdtm = st.st_mtime;
}
cip->lname = file; /* could be NULL */
cip->rname = odstfile;
if (fdtouse >= 0)
cip->useProgressMeter = 0;
FTPStartIOTimer(cip);
/* Note: On Windows, we don't have to do anything special
* for ASCII mode, since Net ASCII's end-of-line sequence
* corresponds to the same thing used for DOS/Windows.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -