]> git.wh0rd.org - dump.git/blob - common/dumprmt.c
Fixed largefile seeks in rmt
[dump.git] / common / dumprmt.c
1 /*
2 * Ported to Linux's Second Extended File System as part of the
3 * dump and restore backup suit
4 * Remy Card <card@Linux.EU.Org>, 1994-1997
5 * Stelian Pop <stelian@popies.net>, 1999-2000
6 * Stelian Pop <stelian@popies.net> - AlcĂ´ve <www.alcove.com>, 2000-2002
7 */
8
9 /*-
10 * Copyright (c) 1980, 1993
11 * The Regents of the University of California. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42 #ifndef lint
43 static const char rcsid[] =
44 "$Id: dumprmt.c,v 1.25 2003/02/12 11:02:29 stelian Exp $";
45 #endif /* not lint */
46
47 #include <config.h>
48 #include <sys/param.h>
49 #include <sys/mtio.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #include <fcntl.h>
53 #ifdef __linux__
54 #include <sys/types.h>
55 #ifdef HAVE_EXT2FS_EXT2_FS_H
56 #include <ext2fs/ext2_fs.h>
57 #else
58 #include <linux/ext2_fs.h>
59 #endif
60 #include <bsdcompat.h>
61 #include <signal.h>
62 #elif defined sunos
63 #include <sys/vnode.h>
64
65 #include <ufs/inode.h>
66 #else
67 #include <ufs/ufs/dinode.h>
68 #endif
69
70 #include <netinet/in.h>
71 #include <netinet/in_systm.h>
72 #include <netinet/ip.h>
73 #include <netinet/tcp.h>
74
75 #include <protocols/dumprestore.h>
76
77 #include <ctype.h>
78 #include <errno.h>
79 #include <compaterr.h>
80 #include <rmtflags.h>
81 #include <netdb.h>
82 #include <pwd.h>
83 #include <stdio.h>
84 #ifdef __STDC__
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #endif
89
90 #ifdef __linux__
91 #include <ext2fs/ext2fs.h>
92 #endif
93
94 #include <pathnames.h>
95 #include "dump.h" /* for X_STARTUP, X_ABORT etc */
96
97 #define TS_CLOSED 0
98 #define TS_OPEN 1
99
100 static int rmtstate = TS_CLOSED;
101 static int tormtape = -1;
102 static int fromrmtape = -1;
103 int rshpid = -1;
104 static const char *rmtpeer = 0;
105
106 static int okname __P((const char *));
107 static OFF_T rmtcall __P((const char *, const char *));
108 static void rmtconnaborted __P((int));
109 static int rmtgetb __P((void));
110 static int rmtgetconn __P((void));
111 static void rmtgets __P((char *, size_t));
112 static OFF_T rmtreply __P((const char *));
113 static int piped_child __P((const char **command));
114 #ifdef KERBEROS
115 int krcmd __P((char **, int /*u_short*/, char *, char *, int *, char *));
116 #endif
117
118 static int errfd = -1;
119 extern int dokerberos;
120 extern int ntrec; /* blocking factor on tape */
121 #ifndef errno
122 extern int errno;
123 #endif
124
125 int
126 rmthost(const char *host)
127 {
128 if (rmtpeer)
129 free((void *)rmtpeer);
130 if ((rmtpeer = strdup(host)) == NULL)
131 rmtpeer = host;
132 signal(SIGPIPE, rmtconnaborted);
133 return rmtgetconn();
134 }
135
136 static void
137 rmtconnaborted(UNUSED(int signo))
138 {
139 msg("Lost connection to remote host.\n");
140 if (errfd != -1) {
141 fd_set r;
142 struct timeval t;
143
144 FD_ZERO(&r);
145 FD_SET(errfd, &r);
146 t.tv_sec = 0;
147 t.tv_usec = 0;
148 if (select(errfd + 1, &r, NULL, NULL, &t)) {
149 int i;
150 char buf[2048];
151
152 if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) {
153 buf[i] = '\0';
154 msg("on %s: %s%s", rmtpeer, buf,
155 buf[i - 1] == '\n' ? "" : "\n");
156 }
157 }
158 }
159
160 exit(X_ABORT);
161 }
162
163 static int
164 rmtgetconn(void)
165 {
166 char *cp;
167 const char *rmt;
168 static struct servent *sp = NULL;
169 static struct passwd *pwd = NULL;
170 const char *tuser;
171 const char *rsh;
172 int size;
173 int throughput;
174 int on;
175 char *rmtpeercopy;
176
177 rsh = getenv("RSH");
178
179 if (!rsh && sp == NULL) {
180 sp = getservbyname(dokerberos ? "kshell" : "shell", "tcp");
181 if (sp == NULL)
182 errx(1, "%s/tcp: unknown service",
183 dokerberos ? "kshell" : "shell");
184 }
185 if (pwd == NULL) {
186 pwd = getpwuid(getuid());
187 if (pwd == NULL)
188 errx(1, "who are you?");
189 }
190 if ((cp = strchr(rmtpeer, '@')) != NULL) {
191 tuser = rmtpeer;
192 *cp = '\0';
193 if (!okname(tuser))
194 exit(X_STARTUP);
195 rmtpeer = ++cp;
196 } else
197 tuser = pwd->pw_name;
198 if ((rmt = getenv("RMT")) == NULL)
199 rmt = _PATH_RMT;
200 msg("");
201
202 if (rsh) {
203 const char *rshcmd[6];
204 rshcmd[0] = rsh;
205 rshcmd[1] = rmtpeer;
206 rshcmd[2] = "-l";
207 rshcmd[3] = tuser;
208 rshcmd[4] = rmt;
209 rshcmd[5] = NULL;
210
211 /* Restore the uid and gid. We really don't want
212 * to execute whatever is put into RSH variable with
213 * more priviledges than needed... */
214 setuid(getuid());
215 setgid(getgid());
216
217 if ((rshpid = piped_child(rshcmd)) < 0) {
218 msg("cannot open connection\n");
219 return 0;
220 }
221 }
222 else {
223 /* Copy rmtpeer to rmtpeercopy to ignore the
224 return value from rcmd. I cannot figure if
225 this is this a bug in rcmd or in my code... */
226 rmtpeercopy = (char *)rmtpeer;
227 #ifdef KERBEROS
228 if (dokerberos)
229 tormtape = krcmd(&rmtpeercopy, sp->s_port, tuser, rmt, &errfd,
230 (char *)0);
231 else
232 #endif
233 tormtape = rcmd(&rmtpeercopy, (u_short)sp->s_port, pwd->pw_name,
234 tuser, rmt, &errfd);
235 if (tormtape < 0) {
236 msg("login to %s as %s failed.\n", rmtpeer, tuser);
237 return 0;
238 }
239 size = ntrec * TP_BSIZE;
240 if (size > 60 * 1024) /* XXX */
241 size = 60 * 1024;
242 /* Leave some space for rmt request/response protocol */
243 size += 2 * 1024;
244 while (size > TP_BSIZE &&
245 setsockopt(tormtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
246 size -= TP_BSIZE;
247 (void)setsockopt(tormtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
248 throughput = IPTOS_THROUGHPUT;
249 if (setsockopt(tormtape, IPPROTO_IP, IP_TOS,
250 &throughput, sizeof(throughput)) < 0)
251 perror("IP_TOS:IPTOS_THROUGHPUT setsockopt");
252 on = 1;
253 if (setsockopt(tormtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
254 perror("TCP_NODELAY setsockopt");
255 fromrmtape = tormtape;
256 }
257 (void)fprintf(stderr, "Connection to %s established.\n", rmtpeer);
258 return 1;
259 }
260
261 static int
262 okname(const char *cp0)
263 {
264 const char *cp;
265 int c;
266
267 for (cp = cp0; *cp; cp++) {
268 c = *cp;
269 if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
270 warnx("invalid user name %s\n", cp0);
271 return (0);
272 }
273 }
274 return (1);
275 }
276
277 int
278 rmtopen(const char *tape, const int mode)
279 {
280 char buf[MAXPATHLEN];
281 char *rmtflags;
282
283 rmtflags = rmtflags_tochar(mode);
284 (void)snprintf(buf, sizeof (buf), "O%s\n%d %s\n",
285 tape,
286 mode & O_ACCMODE,
287 rmtflags);
288 free(rmtflags);
289 rmtstate = TS_OPEN;
290 return (rmtcall(tape, buf));
291 }
292
293 void
294 rmtclose(void)
295 {
296
297 if (rmtstate != TS_OPEN)
298 return;
299 rmtcall("close", "C\n");
300 rmtstate = TS_CLOSED;
301 }
302
303 int
304 rmtread(char *buf, size_t count)
305 {
306 char line[30];
307 int n, i;
308 ssize_t cc;
309
310 (void)snprintf(line, sizeof (line), "R%u\n", (unsigned)count);
311 n = rmtcall("read", line);
312 if (n < 0)
313 /* rmtcall() properly sets errno for us on errors. */
314 return (n);
315 for (i = 0; i < n; i += cc) {
316 cc = read(fromrmtape, buf+i, n - i);
317 if (cc <= 0)
318 rmtconnaborted(0);
319 }
320 return (n);
321 }
322
323 int
324 rmtwrite(const char *buf, size_t count)
325 {
326 char line[30];
327
328 (void)snprintf(line, sizeof (line), "W%ld\n", (long)count);
329 write(tormtape, line, strlen(line));
330 write(tormtape, buf, count);
331 return (rmtreply("write"));
332 }
333
334 OFF_T
335 rmtseek(OFF_T offset, int pos)
336 {
337 char line[80];
338
339 (void)snprintf(line, sizeof (line), "L%lld\n%d\n", (long long)offset, pos);
340 return (rmtcall("seek", line));
341 }
342
343 struct mtget mts;
344
345 struct mtget *
346 rmtstatus(void)
347 {
348 int i;
349 char *cp;
350
351 if (rmtstate != TS_OPEN)
352 return (NULL);
353 rmtcall("status", "S\n");
354 for (i = 0, cp = (char *)&mts; i < (int)sizeof(mts); i++)
355 *cp++ = rmtgetb();
356 return (&mts);
357 }
358
359 int
360 rmtioctl(int cmd, int count)
361 {
362 char buf[256];
363
364 if (count < 0)
365 return (-1);
366 (void)snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count);
367 return (rmtcall("ioctl", buf));
368 }
369
370 static OFF_T
371 rmtcall(const char *cmd, const char *buf)
372 {
373
374 if (write(tormtape, buf, strlen(buf)) != (ssize_t)strlen(buf))
375 rmtconnaborted(0);
376 return (rmtreply(cmd));
377 }
378
379 static OFF_T
380 rmtreply(const char *cmd)
381 {
382 char *cp;
383 char code[30], emsg[BUFSIZ];
384
385 rmtgets(code, sizeof (code));
386 if (*code == 'E' || *code == 'F') {
387 rmtgets(emsg, sizeof (emsg));
388 msg("%s: %s", cmd, emsg);
389 errno = atoi(code + 1);
390 if (*code == 'F')
391 rmtstate = TS_CLOSED;
392 return (-1);
393 }
394 if (*code != 'A') {
395 /* Kill trailing newline */
396 cp = code + strlen(code);
397 if (cp > code && *--cp == '\n')
398 *cp = '\0';
399
400 msg("Protocol to remote tape server botched (code \"%s\").\n",
401 code);
402 rmtconnaborted(0);
403 }
404 return (OFF_T)(atoll(code + 1));
405 }
406
407 static int
408 rmtgetb(void)
409 {
410 char c;
411
412 if (read(fromrmtape, &c, 1) != 1)
413 rmtconnaborted(0);
414 return (c);
415 }
416
417 /* Get a line (guaranteed to have a trailing newline). */
418 static void
419 rmtgets(char *line, size_t len)
420 {
421 char *cp = line;
422
423 while (len > 1) {
424 *cp = rmtgetb();
425 if (*cp == '\n') {
426 cp[1] = '\0';
427 return;
428 }
429 cp++;
430 len--;
431 }
432 *cp = '\0';
433 msg("Protocol to remote tape server botched.\n");
434 msg("(rmtgets got \"%s\").\n", line);
435 rmtconnaborted(0);
436 }
437
438 int piped_child(const char **command) {
439 int pid;
440 int to_child_pipe[2];
441 int from_child_pipe[2];
442
443 if (pipe (to_child_pipe) < 0) {
444 msg ("cannot create pipe: %s\n", strerror(errno));
445 return -1;
446 }
447 if (pipe (from_child_pipe) < 0) {
448 msg ("cannot create pipe: %s\n", strerror(errno));
449 return -1;
450 }
451 pid = fork ();
452 if (pid < 0) {
453 msg ("cannot fork: %s\n", strerror(errno));
454 return -1;
455 }
456 if (pid == 0) {
457 if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0) {
458 msg ("cannot dup2 pipe: %s\n", strerror(errno));
459 exit(1);
460 }
461 if (close (to_child_pipe[1]) < 0) {
462 msg ("cannot close pipe: %s\n", strerror(errno));
463 exit(1);
464 }
465 if (close (from_child_pipe[0]) < 0) {
466 msg ("cannot close pipe: %s\n", strerror(errno));
467 exit(1);
468 }
469 if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0) {
470 msg ("cannot dup2 pipe: %s\n", strerror(errno));
471 exit(1);
472 }
473 setpgid(0, getpid());
474 execvp (command[0], (char *const *) command);
475 msg("cannot exec %s: %s\n", command[0], strerror(errno));
476 exit(1);
477 }
478 if (close (to_child_pipe[0]) < 0) {
479 msg ("cannot close pipe: %s\n", strerror(errno));
480 return -1;
481 }
482 if (close (from_child_pipe[1]) < 0) {
483 msg ("cannot close pipe: %s\n", strerror(errno));
484 return -1;
485 }
486 tormtape = to_child_pipe[1];
487 fromrmtape = from_child_pipe[0];
488 return pid;
489 }