]> Dogcows Code - chaz/tar/blob - src/rtapelib.c
Include full-write.h.
[chaz/tar] / src / rtapelib.c
1 /* Functions for communicating with a remote tape drive.
2 Copyright 1988,92,94,96,97,99,2000, 2001 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18 /* The man page rmt(8) for /etc/rmt documents the remote mag tape protocol
19 which rdump and rrestore use. Unfortunately, the man page is *WRONG*.
20 The author of the routines I'm including originally wrote his code just
21 based on the man page, and it didn't work, so he went to the rdump source
22 to figure out why. The only thing he had to change was to check for the
23 'F' return code in addition to the 'E', and to separate the various
24 arguments with \n instead of a space. I personally don't think that this
25 is much of a problem, but I wanted to point it out. -- Arnold Robbins
26
27 Originally written by Jeff Lee, modified some by Arnold Robbins. Redone
28 as a library that can replace open, read, write, etc., by Fred Fish, with
29 some additional work by Arnold Robbins. Modified to make all rmt* calls
30 into macros for speed by Jay Fenlason. Use -DWITH_REXEC for rexec
31 code, courtesy of Dan Kegel. */
32
33 #include "system.h"
34
35 #include <safe-read.h>
36 #include <full-write.h>
37
38 /* Try hard to get EOPNOTSUPP defined. 486/ISC has it in net/errno.h,
39 3B2/SVR3 has it in sys/inet.h. Otherwise, like on MSDOS, use EINVAL. */
40
41 #ifndef EOPNOTSUPP
42 # if HAVE_NET_ERRNO_H
43 # include <net/errno.h>
44 # endif
45 # if HAVE_SYS_INET_H
46 # include <sys/inet.h>
47 # endif
48 # ifndef EOPNOTSUPP
49 # define EOPNOTSUPP EINVAL
50 # endif
51 #endif
52
53 #include <signal.h>
54
55 #if HAVE_NETDB_H
56 # include <netdb.h>
57 #endif
58
59 #include "rmt.h"
60
61 char *base_name PARAMS ((char const *));
62
63 /* Exit status if exec errors. */
64 #define EXIT_ON_EXEC_ERROR 128
65
66 /* FIXME: Size of buffers for reading and writing commands to rmt. */
67 #define COMMAND_BUFFER_SIZE 64
68
69 #ifndef RETSIGTYPE
70 # define RETSIGTYPE void
71 #endif
72
73 /* FIXME: Maximum number of simultaneous remote tape connections. */
74 #define MAXUNIT 4
75
76 #define PREAD 0 /* read file descriptor from pipe() */
77 #define PWRITE 1 /* write file descriptor from pipe() */
78
79 /* Return the parent's read side of remote tape connection Fd. */
80 #define READ_SIDE(Fd) (from_remote[Fd][PREAD])
81
82 /* Return the parent's write side of remote tape connection Fd. */
83 #define WRITE_SIDE(Fd) (to_remote[Fd][PWRITE])
84
85 /* The pipes for receiving data from remote tape drives. */
86 static int from_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
87
88 /* The pipes for sending data to remote tape drives. */
89 static int to_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
90
91 /* Temporary variable used by macros in rmt.h. */
92 char *rmt_path__;
93 \f
94
95 /* Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE. */
96 static void
97 _rmt_shutdown (int handle, int errno_value)
98 {
99 close (READ_SIDE (handle));
100 close (WRITE_SIDE (handle));
101 READ_SIDE (handle) = -1;
102 WRITE_SIDE (handle) = -1;
103 errno = errno_value;
104 }
105
106 /* Attempt to perform the remote tape command specified in BUFFER on
107 remote tape connection HANDLE. Return 0 if successful, -1 on
108 error. */
109 static int
110 do_command (int handle, const char *buffer)
111 {
112 /* Save the current pipe handler and try to make the request. */
113
114 size_t length = strlen (buffer);
115 RETSIGTYPE (*pipe_handler) () = signal (SIGPIPE, SIG_IGN);
116 ssize_t written = full_write (WRITE_SIDE (handle), buffer, length);
117 signal (SIGPIPE, pipe_handler);
118
119 if (written == length)
120 return 0;
121
122 /* Something went wrong. Close down and go home. */
123
124 _rmt_shutdown (handle, EIO);
125 return -1;
126 }
127
128 static char *
129 get_status_string (int handle, char *command_buffer)
130 {
131 char *cursor;
132 int counter;
133
134 /* Read the reply command line. */
135
136 for (counter = 0, cursor = command_buffer;
137 counter < COMMAND_BUFFER_SIZE;
138 counter++, cursor++)
139 {
140 if (safe_read (READ_SIDE (handle), cursor, 1) != 1)
141 {
142 _rmt_shutdown (handle, EIO);
143 return 0;
144 }
145 if (*cursor == '\n')
146 {
147 *cursor = '\0';
148 break;
149 }
150 }
151
152 if (counter == COMMAND_BUFFER_SIZE)
153 {
154 _rmt_shutdown (handle, EIO);
155 return 0;
156 }
157
158 /* Check the return status. */
159
160 for (cursor = command_buffer; *cursor; cursor++)
161 if (*cursor != ' ')
162 break;
163
164 if (*cursor == 'E' || *cursor == 'F')
165 {
166 errno = atoi (cursor + 1);
167
168 /* Skip the error message line. */
169
170 /* FIXME: there is better to do than merely ignoring error messages
171 coming from the remote end. Translate them, too... */
172
173 {
174 char character;
175
176 while (safe_read (READ_SIDE (handle), &character, 1) == 1)
177 if (character == '\n')
178 break;
179 }
180
181 if (*cursor == 'F')
182 _rmt_shutdown (handle, errno);
183
184 return 0;
185 }
186
187 /* Check for mis-synced pipes. */
188
189 if (*cursor != 'A')
190 {
191 _rmt_shutdown (handle, EIO);
192 return 0;
193 }
194
195 /* Got an `A' (success) response. */
196
197 return cursor + 1;
198 }
199
200 /* Read and return the status from remote tape connection HANDLE. If
201 an error occurred, return -1 and set errno. */
202 static long
203 get_status (int handle)
204 {
205 char command_buffer[COMMAND_BUFFER_SIZE];
206 const char *status = get_status_string (handle, command_buffer);
207 return status ? atol (status) : -1L;
208 }
209
210 static off_t
211 get_status_off (int handle)
212 {
213 char command_buffer[COMMAND_BUFFER_SIZE];
214 const char *status = get_status_string (handle, command_buffer);
215
216 if (! status)
217 return -1;
218 else
219 {
220 /* Parse status, taking care to check for overflow.
221 We can't use standard functions,
222 since off_t might be longer than long. */
223
224 off_t count = 0;
225 int negative;
226
227 for (; *status == ' ' || *status == '\t'; status++)
228 continue;
229
230 negative = *status == '-';
231 status += negative || *status == '+';
232
233 for (;;)
234 {
235 int digit = *status++ - '0';
236 if (9 < (unsigned) digit)
237 break;
238 else
239 {
240 off_t c10 = 10 * count;
241 off_t nc = negative ? c10 - digit : c10 + digit;
242 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
243 return -1;
244 count = nc;
245 }
246 }
247
248 return count;
249 }
250 }
251
252 #if WITH_REXEC
253
254 int rexec ();
255
256 /* Execute /etc/rmt as user USER on remote system HOST using rexec.
257 Return a file descriptor of a bidirectional socket for stdin and
258 stdout. If USER is zero, use the current username.
259
260 By default, this code is not used, since it requires that the user
261 have a .netrc file in his/her home directory, or that the
262 application designer be willing to have rexec prompt for login and
263 password info. This may be unacceptable, and .rhosts files for use
264 with rsh are much more common on BSD systems. */
265 static int
266 _rmt_rexec (char *host, char *user)
267 {
268 int saved_stdin = dup (STDIN_FILENO);
269 int saved_stdout = dup (STDOUT_FILENO);
270 struct servent *rexecserv;
271 int result;
272
273 /* When using cpio -o < filename, stdin is no longer the tty. But the
274 rexec subroutine reads the login and the passwd on stdin, to allow
275 remote execution of the command. So, reopen stdin and stdout on
276 /dev/tty before the rexec and give them back their original value
277 after. */
278
279 if (! freopen ("/dev/tty", "r", stdin))
280 freopen ("/dev/null", "r", stdin);
281 if (! freopen ("/dev/tty", "w", stdout))
282 freopen ("/dev/null", "w", stdout);
283
284 if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv)
285 error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available"));
286
287 result = rexec (&host, rexecserv->s_port, user, 0, "/etc/rmt", 0);
288 if (fclose (stdin) == EOF)
289 error (0, errno, _("stdin"));
290 fdopen (saved_stdin, "r");
291 if (fclose (stdout) == EOF)
292 error (0, errno, _("stdout"));
293 fdopen (saved_stdout, "w");
294
295 return result;
296 }
297
298 #endif /* WITH_REXEC */
299
300 /* Place into BUF a string representing OFLAG, which must be suitable
301 as argument 2 of `open'. BUF must be large enough to hold the
302 result. This function should generate a string that decode_oflag
303 can parse. */
304 static void
305 encode_oflag (char *buf, int oflag)
306 {
307 sprintf (buf, "%d ", oflag);
308
309 switch (oflag & O_ACCMODE)
310 {
311 case O_RDONLY: strcat (buf, "O_RDONLY"); break;
312 case O_RDWR: strcat (buf, "O_RDWR"); break;
313 case O_WRONLY: strcat (buf, "O_WRONLY"); break;
314 default: abort ();
315 }
316
317 #ifdef O_APPEND
318 if (oflag & O_APPEND) strcat (buf, "|O_APPEND");
319 #endif
320 if (oflag & O_CREAT) strcat (buf, "|O_CREAT");
321 #ifdef O_DSYNC
322 if (oflag & O_DSYNC) strcat (buf, "|O_DSYNC");
323 #endif
324 if (oflag & O_EXCL) strcat (buf, "|O_EXCL");
325 #ifdef O_LARGEFILE
326 if (oflag & O_LARGEFILE) strcat (buf, "|O_LARGEFILE");
327 #endif
328 #ifdef O_NOCTTY
329 if (oflag & O_NOCTTY) strcat (buf, "|O_NOCTTY");
330 #endif
331 #ifdef O_NONBLOCK
332 if (oflag & O_NONBLOCK) strcat (buf, "|O_NONBLOCK");
333 #endif
334 #ifdef O_RSYNC
335 if (oflag & O_RSYNC) strcat (buf, "|O_RSYNC");
336 #endif
337 #ifdef O_SYNC
338 if (oflag & O_SYNC) strcat (buf, "|O_SYNC");
339 #endif
340 if (oflag & O_TRUNC) strcat (buf, "|O_TRUNC");
341 }
342
343 /* Open a file (a magnetic tape device?) on the system specified in
344 PATH, as the given user. PATH has the form `[USER@]HOST:FILE'.
345 OPEN_MODE is O_RDONLY, O_WRONLY, etc. If successful, return the
346 remote pipe number plus BIAS. REMOTE_SHELL may be overridden. On
347 error, return -1. */
348 int
349 rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell)
350 {
351 int remote_pipe_number; /* pseudo, biased file descriptor */
352 char *path_copy ; /* copy of path string */
353 char *remote_host; /* remote host name */
354 char *remote_file; /* remote file name (often a device) */
355 char *remote_user; /* remote user name */
356
357 /* Find an unused pair of file descriptors. */
358
359 for (remote_pipe_number = 0;
360 remote_pipe_number < MAXUNIT;
361 remote_pipe_number++)
362 if (READ_SIDE (remote_pipe_number) == -1
363 && WRITE_SIDE (remote_pipe_number) == -1)
364 break;
365
366 if (remote_pipe_number == MAXUNIT)
367 {
368 errno = EMFILE;
369 return -1;
370 }
371
372 /* Pull apart the system and device, and optional user. */
373
374 {
375 char *cursor;
376
377 path_copy = xstrdup (path);
378 remote_host = path_copy;
379 remote_user = 0;
380 remote_file = 0;
381
382 for (cursor = path_copy; *cursor; cursor++)
383 switch (*cursor)
384 {
385 default:
386 break;
387
388 case '\n':
389 /* Do not allow newlines in the path, since the protocol
390 uses newline delimiters. */
391 free (path_copy);
392 errno = ENOENT;
393 return -1;
394
395 case '@':
396 if (!remote_user)
397 {
398 remote_user = remote_host;
399 *cursor = '\0';
400 remote_host = cursor + 1;
401 }
402 break;
403
404 case ':':
405 if (!remote_file)
406 {
407 *cursor = '\0';
408 remote_file = cursor + 1;
409 }
410 break;
411 }
412 }
413
414 /* FIXME: Should somewhat validate the decoding, here. */
415
416 if (remote_user && *remote_user == '\0')
417 remote_user = 0;
418
419 #if WITH_REXEC
420
421 /* Execute the remote command using rexec. */
422
423 READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user);
424 if (READ_SIDE (remote_pipe_number) < 0)
425 {
426 int e = errno;
427 free (path_copy);
428 errno = e;
429 return -1;
430 }
431
432 WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number);
433
434 #else /* not WITH_REXEC */
435 {
436 const char *remote_shell_basename;
437 pid_t status;
438
439 /* Identify the remote command to be executed. */
440
441 if (!remote_shell)
442 {
443 #ifdef REMOTE_SHELL
444 remote_shell = REMOTE_SHELL;
445 #else
446 free (path_copy);
447 errno = EIO;
448 return -1;
449 #endif
450 }
451 remote_shell_basename = base_name (remote_shell);
452
453 /* Set up the pipes for the `rsh' command, and fork. */
454
455 if (pipe (to_remote[remote_pipe_number]) == -1
456 || pipe (from_remote[remote_pipe_number]) == -1)
457 {
458 int e = errno;
459 free (path_copy);
460 errno = e;
461 return -1;
462 }
463
464 status = fork ();
465 if (status == -1)
466 {
467 int e = errno;
468 free (path_copy);
469 errno = e;
470 return -1;
471 }
472
473 if (status == 0)
474 {
475 /* Child. */
476
477 close (STDIN_FILENO);
478 dup (to_remote[remote_pipe_number][PREAD]);
479 close (to_remote[remote_pipe_number][PREAD]);
480 close (to_remote[remote_pipe_number][PWRITE]);
481
482 close (STDOUT_FILENO);
483 dup (from_remote[remote_pipe_number][PWRITE]);
484 close (from_remote[remote_pipe_number][PREAD]);
485 close (from_remote[remote_pipe_number][PWRITE]);
486
487 #if !MSDOS
488 setuid (getuid ());
489 setgid (getgid ());
490 #endif
491
492 if (remote_user)
493 execl (remote_shell, remote_shell_basename, remote_host,
494 "-l", remote_user, "/etc/rmt", (char *) 0);
495 else
496 execl (remote_shell, remote_shell_basename, remote_host,
497 "/etc/rmt", (char *) 0);
498
499 /* Bad problems if we get here. */
500
501 /* In a previous version, _exit was used here instead of exit. */
502 error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell"));
503 }
504
505 /* Parent. */
506
507 close (from_remote[remote_pipe_number][PWRITE]);
508 close (to_remote[remote_pipe_number][PREAD]);
509 }
510 #endif /* not WITH_REXEC */
511
512 /* Attempt to open the tape device. */
513
514 {
515 size_t remote_file_len = strlen (remote_file);
516 char *command_buffer = xmalloc (remote_file_len + 1000);
517 sprintf (command_buffer, "O%s\n", remote_file);
518 encode_oflag (command_buffer + remote_file_len + 2, open_mode);
519 strcat (command_buffer, "\n");
520 if (do_command (remote_pipe_number, command_buffer) == -1
521 || get_status (remote_pipe_number) == -1)
522 {
523 int e = errno;
524 free (command_buffer);
525 free (path_copy);
526 _rmt_shutdown (remote_pipe_number, e);
527 return -1;
528 }
529 free (command_buffer);
530 }
531
532 free (path_copy);
533 return remote_pipe_number + bias;
534 }
535
536 /* Close remote tape connection HANDLE and shut down. Return 0 if
537 successful, -1 on error. */
538 int
539 rmt_close__ (int handle)
540 {
541 int status;
542
543 if (do_command (handle, "C\n") == -1)
544 return -1;
545
546 status = get_status (handle);
547 _rmt_shutdown (handle, errno);
548 return status;
549 }
550
551 /* Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE.
552 Return the number of bytes read on success, -1 on error. */
553 ssize_t
554 rmt_read__ (int handle, char *buffer, size_t length)
555 {
556 char command_buffer[COMMAND_BUFFER_SIZE];
557 ssize_t status, rlen;
558 size_t counter;
559
560 sprintf (command_buffer, "R%lu\n", (unsigned long) length);
561 if (do_command (handle, command_buffer) == -1
562 || (status = get_status (handle)) == -1)
563 return -1;
564
565 for (counter = 0; counter < status; counter += rlen, buffer += rlen)
566 {
567 rlen = safe_read (READ_SIDE (handle), buffer, status - counter);
568 if (rlen <= 0)
569 {
570 _rmt_shutdown (handle, EIO);
571 return -1;
572 }
573 }
574
575 return status;
576 }
577
578 /* Write LENGTH bytes from BUFFER to remote tape connection HANDLE.
579 Return the number of bytes written on success, -1 on error. */
580 ssize_t
581 rmt_write__ (int handle, char *buffer, size_t length)
582 {
583 char command_buffer[COMMAND_BUFFER_SIZE];
584 RETSIGTYPE (*pipe_handler) ();
585 size_t written;
586
587 sprintf (command_buffer, "W%lu\n", (unsigned long) length);
588 if (do_command (handle, command_buffer) == -1)
589 return -1;
590
591 pipe_handler = signal (SIGPIPE, SIG_IGN);
592 written = full_write (WRITE_SIDE (handle), buffer, length);
593 signal (SIGPIPE, pipe_handler);
594 if (written == length)
595 return get_status (handle);
596
597 /* Write error. */
598
599 _rmt_shutdown (handle, EIO);
600 return -1;
601 }
602
603 /* Perform an imitation lseek operation on remote tape connection
604 HANDLE. Return the new file offset if successful, -1 if on error. */
605 off_t
606 rmt_lseek__ (int handle, off_t offset, int whence)
607 {
608 char command_buffer[COMMAND_BUFFER_SIZE];
609 char operand_buffer[UINTMAX_STRSIZE_BOUND];
610 uintmax_t u = offset < 0 ? - (uintmax_t) offset : (uintmax_t) offset;
611 char *p = operand_buffer + sizeof operand_buffer;
612
613 do
614 *--p = '0' + (int) (u % 10);
615 while ((u /= 10) != 0);
616 if (offset < 0)
617 *--p = '-';
618
619 switch (whence)
620 {
621 case SEEK_SET: whence = 0; break;
622 case SEEK_CUR: whence = 1; break;
623 case SEEK_END: whence = 2; break;
624 default: abort ();
625 }
626
627 sprintf (command_buffer, "L%s\n%d\n", p, whence);
628
629 if (do_command (handle, command_buffer) == -1)
630 return -1;
631
632 return get_status_off (handle);
633 }
634
635 /* Perform a raw tape operation on remote tape connection HANDLE.
636 Return the results of the ioctl, or -1 on error. */
637 int
638 rmt_ioctl__ (int handle, int operation, char *argument)
639 {
640 switch (operation)
641 {
642 default:
643 errno = EOPNOTSUPP;
644 return -1;
645
646 #ifdef MTIOCTOP
647 case MTIOCTOP:
648 {
649 char command_buffer[COMMAND_BUFFER_SIZE];
650 char operand_buffer[UINTMAX_STRSIZE_BOUND];
651 uintmax_t u = (((struct mtop *) argument)->mt_count < 0
652 ? - (uintmax_t) ((struct mtop *) argument)->mt_count
653 : (uintmax_t) ((struct mtop *) argument)->mt_count);
654 char *p = operand_buffer + sizeof operand_buffer;
655
656 do
657 *--p = '0' + (int) (u % 10);
658 while ((u /= 10) != 0);
659 if (((struct mtop *) argument)->mt_count < 0)
660 *--p = '-';
661
662 /* MTIOCTOP is the easy one. Nothing is transferred in binary. */
663
664 sprintf (command_buffer, "I%d\n%s\n",
665 ((struct mtop *) argument)->mt_op, p);
666 if (do_command (handle, command_buffer) == -1)
667 return -1;
668
669 return get_status (handle);
670 }
671 #endif /* MTIOCTOP */
672
673 #ifdef MTIOCGET
674 case MTIOCGET:
675 {
676 ssize_t status;
677 ssize_t counter;
678
679 /* Grab the status and read it directly into the structure. This
680 assumes that the status buffer is not padded and that 2 shorts
681 fit in a long without any word alignment problems; i.e., the
682 whole struct is contiguous. NOTE - this is probably NOT a good
683 assumption. */
684
685 if (do_command (handle, "S") == -1
686 || (status = get_status (handle), status == -1))
687 return -1;
688
689 for (; status > 0; status -= counter, argument += counter)
690 {
691 counter = safe_read (READ_SIDE (handle), argument, status);
692 if (counter <= 0)
693 {
694 _rmt_shutdown (handle, EIO);
695 return -1;
696 }
697 }
698
699 /* Check for byte position. mt_type (or mt_model) is a small integer
700 field (normally) so we will check its magnitude. If it is larger
701 than 256, we will assume that the bytes are swapped and go through
702 and reverse all the bytes. */
703
704 if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256)
705 return 0;
706
707 for (counter = 0; counter < status; counter += 2)
708 {
709 char copy = argument[counter];
710
711 argument[counter] = argument[counter + 1];
712 argument[counter + 1] = copy;
713 }
714
715 return 0;
716 }
717 #endif /* MTIOCGET */
718
719 }
720 }
This page took 0.066692 seconds and 5 git commands to generate.