/ncftp-3.2.5/libncftp/io_getmem.c

# · C · 268 lines · 209 code · 28 blank · 31 comment · 98 complexity · f2aefca7baa65e4454161d6da6f2f25a MD5 · raw file

  1. /* io_getmem.c
  2. *
  3. * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software.
  4. * All rights reserved.
  5. *
  6. */
  7. #include "syshdrs.h"
  8. #ifdef PRAGMA_HDRSTOP
  9. # pragma hdrstop
  10. #endif
  11. #ifndef NO_SIGNALS
  12. # define NO_SIGNALS 1
  13. #endif
  14. int
  15. FTPGetFileToMemory(
  16. const FTPCIPtr cip,
  17. const char *const file,
  18. char *memBuf,
  19. const size_t maxNumberOfBytesToWriteToMemBuf,
  20. size_t *const numberOfBytesWrittenToMemBuf,
  21. const longest_int startPoint,
  22. const int deleteflag
  23. )
  24. {
  25. int tmpResult;
  26. volatile int result;
  27. int atEOF;
  28. longest_int expectedSize;
  29. size_t ntoread;
  30. read_return_t nread;
  31. size_t numberOfBytesLeftInMemBuf;
  32. #if !defined(NO_SIGNALS)
  33. volatile FTPSigProc osigpipe;
  34. volatile FTPCIPtr vcip;
  35. int sj;
  36. #endif /* NO_SIGNALS */
  37. result = kNoErr;
  38. atEOF = 1;
  39. cip->usingTAR = 0;
  40. numberOfBytesLeftInMemBuf = maxNumberOfBytesToWriteToMemBuf;
  41. if (numberOfBytesWrittenToMemBuf != NULL)
  42. *numberOfBytesWrittenToMemBuf = 0;
  43. if ((file == NULL) || (file[0] == '\0') || (memBuf == NULL) || (maxNumberOfBytesToWriteToMemBuf == 0)) {
  44. return (kErrBadParameter);
  45. }
  46. FTPCheckForRestartModeAvailability(cip);
  47. if ((startPoint != 0) && (cip->hasREST == kCommandNotAvailable)) {
  48. cip->errNo = kErrRESTNotAvailable;
  49. return (cip->errNo);
  50. }
  51. (void) FTPFileSize(cip, file, &expectedSize, kTypeBinary);
  52. if ((expectedSize != (longest_int) 0) && (startPoint > expectedSize)) {
  53. /* Don't go to all the trouble of downloading nothing. */
  54. if (deleteflag == kDeleteYes)
  55. (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  56. return (kNoErr);
  57. }
  58. if ((cip->numDownloads == 0) && (cip->dataSocketRBufSize != 0)) {
  59. /* If dataSocketSBufSize is non-zero, it means you
  60. * want to explicitly try to set the size of the
  61. * socket's I/O buffer.
  62. *
  63. * If it is zero, it means you want to just use the
  64. * TCP stack's default value, which is typically
  65. * between 8 and 64 kB.
  66. *
  67. * If you try to set the buffer larger than 64 kB,
  68. * the TCP stack should try to use RFC 1323 to
  69. * negotiate "TCP Large Windows" which may yield
  70. * significant performance gains.
  71. */
  72. if (cip->hasSITE_RETRBUFSIZE == kCommandAvailable)
  73. (void) FTPCmd(cip, "SITE RETRBUFSIZE %lu", (unsigned long) cip->dataSocketRBufSize);
  74. else if (cip->hasSITE_RBUFSIZ == kCommandAvailable)
  75. (void) FTPCmd(cip, "SITE RBUFSIZ %lu", (unsigned long) cip->dataSocketRBufSize);
  76. else if (cip->hasSITE_RBUFSZ == kCommandAvailable)
  77. (void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketRBufSize);
  78. else if (cip->hasSITE_BUFSIZE == kCommandAvailable)
  79. (void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
  80. }
  81. #ifdef NO_SIGNALS
  82. #else /* NO_SIGNALS */
  83. vcip = cip;
  84. osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
  85. gGotBrokenData = 0;
  86. gCanBrokenDataJmp = 0;
  87. #ifdef HAVE_SIGSETJMP
  88. sj = sigsetjmp(gBrokenDataJmp, 1);
  89. #else
  90. sj = setjmp(gBrokenDataJmp);
  91. #endif /* HAVE_SIGSETJMP */
  92. if (sj != 0) {
  93. (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  94. FTPShutdownHost(vcip);
  95. vcip->errNo = kErrRemoteHostClosedConnection;
  96. return(vcip->errNo);
  97. }
  98. gCanBrokenDataJmp = 1;
  99. #endif /* NO_SIGNALS */
  100. tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, startPoint, "RETR %s", file);
  101. if (tmpResult < 0) {
  102. result = tmpResult;
  103. if (result == kErrGeneric)
  104. result = kErrRETRFailed;
  105. cip->errNo = result;
  106. #if !defined(NO_SIGNALS)
  107. (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  108. #endif /* NO_SIGNALS */
  109. return (result);
  110. }
  111. if ((startPoint != 0) && (cip->startPoint == 0)) {
  112. /* Remote could not or would not set the start offset
  113. * to what we wanted.
  114. */
  115. cip->errNo = kErrSetStartPoint;
  116. #if !defined(NO_SIGNALS)
  117. (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  118. #endif /* NO_SIGNALS */
  119. return (cip->errNo);
  120. }
  121. FTPInitIOTimer(cip);
  122. cip->expectedSize = expectedSize;
  123. cip->lname = NULL; /* could be NULL */
  124. cip->rname = file;
  125. FTPStartIOTimer(cip);
  126. /* Binary */
  127. for (;;) {
  128. if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */
  129. cip->errNo = result = kErrDataTimedOut;
  130. FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
  131. break;
  132. }
  133. if (cip->cancelXfer > 0) {
  134. FTPAbortDataTransfer(cip);
  135. result = cip->errNo = kErrDataTransferAborted;
  136. break;
  137. }
  138. ntoread = numberOfBytesLeftInMemBuf;
  139. if (ntoread > cip->bufSize)
  140. ntoread = cip->bufSize; /* Break it up into blocks. */
  141. #ifdef NO_SIGNALS
  142. nread = (read_return_t) SRead(cip->dataSocket, memBuf, ntoread, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
  143. if (nread == kTimeoutErr) {
  144. cip->errNo = result = kErrDataTimedOut;
  145. FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
  146. break;
  147. } else if (nread < 0) {
  148. if (errno == EPIPE) {
  149. result = cip->errNo = kErrSocketReadFailed;
  150. errno = EPIPE;
  151. FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
  152. } else if (errno == EINTR) {
  153. continue;
  154. } else {
  155. FTPLogError(cip, kDoPerror, "Remote read failed.\n");
  156. result = kErrSocketReadFailed;
  157. cip->errNo = kErrSocketReadFailed;
  158. }
  159. break;
  160. } else if (nread == 0) {
  161. break;
  162. }
  163. #else
  164. gCanBrokenDataJmp = 1;
  165. if (cip->xferTimeout > 0)
  166. (void) alarm(cip->xferTimeout);
  167. nread = read(cip->dataSocket, memBuf, (read_size_t) ntoread);
  168. if (nread < 0) {
  169. if ((gGotBrokenData != 0) || (errno == EPIPE)) {
  170. result = cip->errNo = kErrSocketReadFailed;
  171. errno = EPIPE;
  172. FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
  173. } else if (errno == EINTR) {
  174. continue;
  175. } else {
  176. result = cip->errNo = kErrSocketReadFailed;
  177. FTPLogError(cip, kDoPerror, "Remote read failed.\n");
  178. }
  179. (void) shutdown(cip->dataSocket, 2);
  180. break;
  181. } else if (nread == 0) {
  182. /* At EOF. */
  183. break;
  184. }
  185. gCanBrokenDataJmp = 0;
  186. #endif /* NO_SIGNALS */
  187. memBuf += nread;
  188. if (numberOfBytesWrittenToMemBuf != NULL)
  189. *numberOfBytesWrittenToMemBuf += (size_t) nread;
  190. cip->bytesTransferred += (longest_int) nread;
  191. FTPUpdateIOTimer(cip);
  192. if ((size_t) nread > numberOfBytesLeftInMemBuf) {
  193. /* assertion failure */
  194. result = cip->errNo = kErrBugInLibrary;
  195. break;
  196. }
  197. numberOfBytesLeftInMemBuf -= nread;
  198. if (numberOfBytesLeftInMemBuf == 0) {
  199. /* Done (but maybe not at EOF of remote file). */
  200. atEOF = 0;
  201. if ((cip->bytesTransferred + startPoint) == expectedSize)
  202. atEOF = 1;
  203. break;
  204. }
  205. }
  206. #if !defined(NO_SIGNALS)
  207. if (cip->xferTimeout > 0)
  208. (void) alarm(0);
  209. gCanBrokenDataJmp = 0;
  210. #endif /* NO_SIGNALS */
  211. /* If there hasn't been an error, and you limited
  212. * the number of bytes, we need to abort the
  213. * remaining data.
  214. */
  215. if ((result == kNoErr) && (atEOF == 0)) {
  216. FTPAbortDataTransfer(cip);
  217. tmpResult = FTPEndDataCmd(cip, 1);
  218. if ((tmpResult < 0) && (result == 0) && (tmpResult != kErrDataTransferFailed)) {
  219. result = kErrRETRFailed;
  220. cip->errNo = kErrRETRFailed;
  221. }
  222. } else {
  223. tmpResult = FTPEndDataCmd(cip, 1);
  224. if ((tmpResult < 0) && (result == 0)) {
  225. result = kErrRETRFailed;
  226. cip->errNo = kErrRETRFailed;
  227. }
  228. }
  229. FTPStopIOTimer(cip);
  230. #if !defined(NO_SIGNALS)
  231. (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  232. #endif /* NO_SIGNALS */
  233. if (result == kNoErr) {
  234. cip->numDownloads++;
  235. if (deleteflag == kDeleteYes) {
  236. result = FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  237. }
  238. }
  239. return (result);
  240. } /* FTPGetOneF */