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