/ncftp-3.2.5/libncftp/io_gettar.c
C | 367 lines | 298 code | 46 blank | 23 comment | 111 complexity | 5baddd7bda0add6b86ce517c69e4a6ad MD5 | raw file
Possible License(s): AGPL-3.0
1/* io_gettar.c
2 *
3 * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software.
4 * All rights reserved.
5 *
6 */
7
8#include "syshdrs.h"
9#ifdef PRAGMA_HDRSTOP
10# pragma hdrstop
11#endif
12
13#if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
14# define ASCII_TRANSLATION 0
15#endif
16
17#ifndef ASCII_TRANSLATION
18# define ASCII_TRANSLATION 1
19#endif
20
21#ifndef NO_SIGNALS
22# define NO_SIGNALS 1
23#endif
24
25#ifndef O_BINARY
26 /* Needed for platforms using different EOLN sequence (i.e. DOS) */
27# ifdef _O_BINARY
28# define O_BINARY _O_BINARY
29# else
30# define O_BINARY 0
31# endif
32#endif
33
34/* Nice for UNIX, but not necessary otherwise. */
35#ifdef TAR
36
37static int
38OpenTar(const FTPCIPtr cip, const char *const dstdir, int *const pid)
39{
40 int pipe1[2];
41 int pid1;
42 int i;
43 char *argv[8];
44
45 *pid = -1;
46
47 if (access(TAR, X_OK) < 0) {
48 /* Path to TAR is invalid. */
49 return (-1);
50 }
51
52 if (pipe(pipe1) < 0) {
53 FTPLogError(cip, kDoPerror, "pipe to Tar failed");
54 return (-1);
55 }
56
57 pid1 = (int) fork();
58 if (pid1 < 0) {
59 (void) close(pipe1[0]);
60 (void) close(pipe1[1]);
61 return (-1);
62 } else if (pid1 == 0) {
63 /* Child */
64 if ((dstdir != NULL) && (dstdir[0] != '\0') && (chdir(dstdir) < 0)) {
65 FTPLogError(cip, kDoPerror, "tar chdir to %s failed", dstdir);
66 exit(1);
67 }
68 (void) close(pipe1[1]); /* close write end */
69 (void) dup2(pipe1[0], 0); /* use read end on stdin */
70 (void) close(pipe1[0]);
71
72 for (i=3; i<256; i++)
73 (void) close(i);
74
75 argv[0] = strdup("tar");
76 argv[1] = strdup("xpf");
77 argv[2] = strdup("-");
78 argv[3] = NULL;
79
80 (void) execv(TAR, argv);
81 exit(1);
82 }
83
84 /* Parent */
85 *pid = pid1;
86
87 (void) close(pipe1[0]); /* close read end */
88 return (pipe1[1]); /* use write end */
89} /* OpenTar */
90
91
92
93
94int
95FTPGetOneTarF(const FTPCIPtr cip, const char *file, const char *const dstdir)
96{
97 char *buf;
98 size_t bufSize;
99 int tmpResult;
100 volatile int result;
101 read_return_t nread;
102 write_return_t nwrote;
103 volatile int fd;
104 volatile int vfd;
105 const char *volatile vfile;
106#ifndef NO_SIGNALS
107 int sj;
108 volatile FTPSigProc osigpipe;
109 volatile FTPCIPtr vcip;
110#endif
111 int pid, status;
112 char savedCwd[512];
113 char *volatile basecp;
114
115 result = kNoErr;
116 cip->usingTAR = 0;
117
118 if (cip->hasRETR_tar == kCommandNotAvailable) {
119 result = kErrOpenFailed;
120 cip->errNo = kErrOpenFailed;
121 return (result);
122 }
123
124 if ((file[0] == '\0') || ((file[0] == '/') && (file[1] == '\0'))) {
125 /* It was "/"
126 * We can't do that, because "get /.tar"
127 * or "get .tar" does not work.
128 */
129 result = kErrOpenFailed;
130 cip->errNo = kErrOpenFailed;
131 return (result);
132 }
133
134 if (FTPCmd(cip, "MDTM %s.tar", file) == 2) {
135 /* Better not use this method since there is
136 * no way to tell if the server would use the
137 * existing .tar or do a new one on the fly.
138 */
139 result = kErrOpenFailed;
140 cip->errNo = kErrOpenFailed;
141 return (result);
142 }
143
144 basecp = strrchr(file, '/');
145 if (basecp != NULL)
146 basecp = strrchr(file, '\\');
147 if (basecp != NULL) {
148 /* Need to cd to the parent directory and get it
149 * from there.
150 */
151 if (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != 0) {
152 result = kErrOpenFailed;
153 cip->errNo = kErrOpenFailed;
154 return (result);
155 }
156 result = FTPChdir(cip, file);
157 if (result != kNoErr) {
158 return (result);
159 }
160 result = FTPChdir(cip, "..");
161 if (result != kNoErr) {
162 (void) FTPChdir(cip, savedCwd);
163 return (result);
164 }
165 file = basecp + 1;
166 }
167
168 fd = OpenTar(cip, dstdir, &pid);
169 if (fd < 0) {
170 result = kErrOpenFailed;
171 cip->errNo = kErrOpenFailed;
172 if (basecp != NULL)
173 (void) FTPChdir(cip, savedCwd);
174 return (result);
175 }
176
177 vfd = fd;
178 vfile = file;
179
180#ifndef NO_SIGNALS
181 vcip = cip;
182 osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
183
184 gGotBrokenData = 0;
185 gCanBrokenDataJmp = 0;
186
187#ifdef HAVE_SIGSETJMP
188 sj = sigsetjmp(gBrokenDataJmp, 1);
189#else
190 sj = setjmp(gBrokenDataJmp);
191#endif /* HAVE_SIGSETJMP */
192
193 if (sj != 0) {
194 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
195 FTPShutdownHost(vcip);
196
197 (void) signal(SIGPIPE, SIG_IGN);
198 (void) close(vfd);
199 for (;;) {
200#ifdef HAVE_WAITPID
201 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
202 break;
203#else
204 if ((wait(&status) < 0) && (errno != EINTR))
205 break;
206#endif
207 if (WIFEXITED(status) || WIFSIGNALED(status))
208 break; /* done */
209 }
210 if (basecp != NULL)
211 (void) FTPChdir(cip, savedCwd);
212 vcip->errNo = kErrRemoteHostClosedConnection;
213 return(vcip->errNo);
214 }
215 gCanBrokenDataJmp = 1;
216
217#endif /* NO_SIGNALS */
218
219 tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, (longest_int) 0, "RETR %s.tar", vfile);
220
221 if (tmpResult < 0) {
222 result = tmpResult;
223 if (result == kErrGeneric)
224 result = kErrRETRFailed;
225 cip->errNo = result;
226 if (cip->hasRETR_tar == kCommandAvailabilityUnknown)
227 cip->hasRETR_tar = kCommandNotAvailable;
228
229#ifndef NO_SIGNALS
230 (void) signal(SIGPIPE, SIG_IGN);
231#endif
232 (void) close(vfd);
233 for (;;) {
234#ifdef HAVE_WAITPID
235 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
236 break;
237#else
238 if ((wait(&status) < 0) && (errno != EINTR))
239 break;
240#endif
241 if (WIFEXITED(status) || WIFSIGNALED(status))
242 break; /* done */
243 }
244
245#ifndef NO_SIGNALS
246 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
247#endif
248 if (basecp != NULL)
249 (void) FTPChdir(cip, savedCwd);
250 return (result);
251 }
252
253 if (cip->hasRETR_tar == kCommandAvailabilityUnknown)
254 cip->hasRETR_tar = kCommandAvailable;
255 cip->usingTAR = 1;
256 buf = cip->buf;
257 bufSize = cip->bufSize;
258
259 FTPInitIOTimer(cip);
260 cip->lname = vfile; /* could be NULL */
261 cip->rname = vfile;
262 FTPStartIOTimer(cip);
263
264 /* Binary */
265 for (;;) {
266 if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */
267 cip->errNo = result = kErrDataTimedOut;
268 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
269 break;
270 }
271 if (cip->cancelXfer > 0) {
272 FTPAbortDataTransfer(cip);
273 result = cip->errNo = kErrDataTransferAborted;
274 break;
275 }
276#if !defined(NO_SIGNALS)
277 gCanBrokenDataJmp = 1;
278 if (cip->xferTimeout > 0)
279 (void) alarm(cip->xferTimeout);
280#endif /* NO_SIGNALS */
281#ifdef NO_SIGNALS
282 nread = (read_return_t) SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
283 if (nread == kTimeoutErr) {
284 cip->errNo = result = kErrDataTimedOut;
285 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
286 break;
287 } else if (nread < 0) {
288 if (errno == EINTR)
289 continue;
290 FTPLogError(cip, kDoPerror, "Remote read failed.\n");
291 result = kErrSocketReadFailed;
292 cip->errNo = kErrSocketReadFailed;
293 break;
294 } else if (nread == 0) {
295 break;
296 }
297#else
298 nread = read(cip->dataSocket, buf, (read_size_t) bufSize);
299 if (nread < 0) {
300 if (errno == EINTR)
301 continue;
302 FTPLogError(cip, kDoPerror, "Remote read failed.\n");
303 result = kErrSocketReadFailed;
304 cip->errNo = kErrSocketReadFailed;
305 break;
306 } else if (nread == 0) {
307 break;
308 }
309 gCanBrokenDataJmp = 0;
310#endif
311
312 nwrote = write(fd, buf, (write_size_t) nread);
313 if (nwrote != nread) {
314 if (errno == EPIPE) {
315 result = kErrWriteFailed;
316 cip->errNo = kErrWriteFailed;
317 errno = EPIPE;
318 } else {
319 FTPLogError(cip, kDoPerror, "Local write failed.\n");
320 result = kErrWriteFailed;
321 cip->errNo = kErrWriteFailed;
322 }
323 break;
324 }
325 cip->bytesTransferred += (longest_int) nread;
326 FTPUpdateIOTimer(cip);
327 }
328
329#if !defined(NO_SIGNALS)
330 if (cip->xferTimeout > 0)
331 (void) alarm(0);
332 gCanBrokenDataJmp = 0;
333#endif /* NO_SIGNALS */
334
335 (void) close(fd);
336 for (;;) {
337#ifdef HAVE_WAITPID
338 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
339 break;
340#else
341 if ((wait(&status) < 0) && (errno != EINTR))
342 break;
343#endif
344 if (WIFEXITED(status) || WIFSIGNALED(status))
345 break; /* done */
346 }
347
348 tmpResult = FTPEndDataCmd(cip, 1);
349 if ((tmpResult < 0) && (result == 0)) {
350 result = kErrRETRFailed;
351 cip->errNo = kErrRETRFailed;
352 }
353 FTPStopIOTimer(cip);
354#if !defined(NO_SIGNALS)
355 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
356#endif
357
358 if ((result == 0) && (cip->bytesTransferred == 0)) {
359 result = kErrOpenFailed;
360 cip->errNo = kErrOpenFailed;
361 }
362 if (basecp != NULL)
363 (void) FTPChdir(cip, savedCwd);
364 return (result);
365} /* FTPGetOneTarF */
366
367#endif /* TAR */