Fix a bug when '-f -H' is used and the target already exists. cpdup was
[dragonfly.git] / contrib / tar / src / rmt.c
1 /* Remote connection server.
2
3    Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001 Free Software
4    Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any later
9    version.
10
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
14    Public License for more details.
15
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 /* Copyright (C) 1983 Regents of the University of California.
21    All rights reserved.
22
23    Redistribution and use in source and binary forms are permitted provided
24    that the above copyright notice and this paragraph are duplicated in all
25    such forms and that any documentation, advertising materials, and other
26    materials related to such distribution and use acknowledge that the
27    software was developed by the University of California, Berkeley.  The
28    name of the University may not be used to endorse or promote products
29    derived from this software without specific prior written permission.
30    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
31    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
32    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  */
33
34 #include "system.h"
35 #include <print-copyr.h>
36 #include <localedir.h>
37 #include <safe-read.h>
38 #include <full-write.h>
39
40 #include <getopt.h>
41 #include <sys/socket.h>
42
43 #ifndef EXIT_FAILURE
44 # define EXIT_FAILURE 1
45 #endif
46 #ifndef EXIT_SUCCESS
47 # define EXIT_SUCCESS 0
48 #endif
49
50 /* Maximum size of a string from the requesting program.  */
51 #define STRING_SIZE 64
52
53 /* Name of executing program.  */
54 const char *program_name;
55
56 /* File descriptor of the tape device, or negative if none open.  */
57 static int tape = -1;
58
59 /* Buffer containing transferred data, and its allocated size.  */
60 static char *record_buffer;
61 static size_t allocated_size;
62
63 /* Buffer for constructing the reply.  */
64 static char reply_buffer[BUFSIZ];
65
66 /* Debugging tools.  */
67
68 static FILE *debug_file;
69
70 #define DEBUG(File) \
71   if (debug_file) fprintf(debug_file, File)
72
73 #define DEBUG1(File, Arg) \
74   if (debug_file) fprintf(debug_file, File, Arg)
75
76 #define DEBUG2(File, Arg1, Arg2) \
77   if (debug_file) fprintf(debug_file, File, Arg1, Arg2)
78
79 /* Return an error string, given an error number.  */
80 #if HAVE_STRERROR
81 # ifndef strerror
82 char *strerror ();
83 # endif
84 #else
85 static char *
86 private_strerror (int errnum)
87 {
88   extern char *sys_errlist[];
89   extern int sys_nerr;
90
91   if (errnum > 0 && errnum <= sys_nerr)
92     return _(sys_errlist[errnum]);
93   return _("Unknown system error");
94 }
95 # define strerror private_strerror
96 #endif
97
98 static void
99 report_error_message (const char *string)
100 {
101   DEBUG1 ("rmtd: E 0 (%s)\n", string);
102
103   sprintf (reply_buffer, "E0\n%s\n", string);
104   full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
105 }
106
107 static void
108 report_numbered_error (int num)
109 {
110   DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num));
111
112   sprintf (reply_buffer, "E%d\n%s\n", num, strerror (num));
113   full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
114 }
115
116 static void
117 get_string (char *string)
118 {
119   int counter;
120
121   for (counter = 0; counter < STRING_SIZE; counter++)
122     {
123       if (safe_read (STDIN_FILENO, string + counter, 1) != 1)
124         exit (EXIT_SUCCESS);
125
126       if (string[counter] == '\n')
127         break;
128     }
129   string[counter] = '\0';
130 }
131
132 static void
133 prepare_record_buffer (size_t size)
134 {
135   if (size <= allocated_size)
136     return;
137
138   if (record_buffer)
139     free (record_buffer);
140
141   record_buffer = malloc (size);
142
143   if (! record_buffer)
144     {
145       DEBUG (_("rmtd: Cannot allocate buffer space\n"));
146
147       report_error_message (N_("Cannot allocate buffer space"));
148       exit (EXIT_FAILURE);      /* exit status used to be 4 */
149     }
150
151   allocated_size = size;
152
153 #ifdef SO_RCVBUF
154   while (size > 1024 &&
155          (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_RCVBUF,
156                       (char *) &size, sizeof size)
157           < 0))
158     size -= 1024;
159 #else
160   /* FIXME: I do not see any purpose to the following line...  Sigh! */
161   size = 1 + ((size - 1) % 1024);
162 #endif
163 }
164
165 /* Decode OFLAG_STRING, which represents the 2nd argument to `open'.
166    OFLAG_STRING should contain an optional integer, followed by an optional
167    symbolic representation of an open flag using only '|' to separate its
168    components (e.g. "O_WRONLY|O_CREAT|O_TRUNC").  Prefer the symbolic
169    representation if available, falling back on the numeric
170    representation, or to zero if both formats are absent.
171
172    This function should be the inverse of encode_oflag.  The numeric
173    representation is not portable from one host to another, but it is
174    for backward compatibility with old-fashioned clients that do not
175    emit symbolic open flags.  */
176
177 static int
178 decode_oflag (char const *oflag_string)
179 {
180   char *oflag_num_end;
181   int numeric_oflag = strtol (oflag_string, &oflag_num_end, 10);
182   int symbolic_oflag = 0;
183   
184   oflag_string = oflag_num_end;
185   while (ISSPACE ((unsigned char) *oflag_string))
186     oflag_string++;
187     
188   do
189     {
190       struct name_value_pair { char const *name; int value; };
191       static struct name_value_pair const table[] =
192       {
193 #ifdef O_APPEND
194         {"APPEND", O_APPEND},
195 #endif
196         {"CREAT", O_CREAT},
197 #ifdef O_DSYNC
198         {"DSYNC", O_DSYNC},
199 #endif
200         {"EXCL", O_EXCL},
201 #ifdef O_LARGEFILE
202         {"LARGEFILE", O_LARGEFILE}, /* LFS extension for opening large files */
203 #endif
204 #ifdef O_NOCTTY
205         {"NOCTTY", O_NOCTTY},
206 #endif
207 #ifdef O_NONBLOCK
208         {"NONBLOCK", O_NONBLOCK},
209 #endif
210         {"RDONLY", O_RDONLY},
211         {"RDWR", O_RDWR},
212 #ifdef O_RSYNC
213         {"RSYNC", O_RSYNC},
214 #endif
215 #ifdef O_SYNC
216         {"SYNC", O_SYNC},
217 #endif
218         {"TRUNC", O_TRUNC},
219         {"WRONLY", O_WRONLY}
220       };
221       struct name_value_pair const *t;
222       size_t s;
223
224       if (*oflag_string++ != 'O' || *oflag_string++ != '_')
225         return numeric_oflag;
226
227       for (t = table;
228            (strncmp (oflag_string, t->name, s = strlen (t->name)) != 0
229             || (oflag_string[s]
230                 && strchr ("ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789",
231                            oflag_string[s])));
232            t++)
233         if (t == table + sizeof table / sizeof *table - 1)
234           return numeric_oflag;
235
236       symbolic_oflag |= t->value;
237       oflag_string += s;
238     }
239   while (*oflag_string++ == '|');
240
241   return symbolic_oflag;
242 }
243
244 static struct option const long_opts[] =
245 {
246   {"help", no_argument, 0, 'h'},
247   {"version", no_argument, 0, 'v'},
248   {0, 0, 0, 0}
249 };
250
251 static void
252 usage (int status)
253 {
254   if (status != EXIT_SUCCESS)
255     fprintf (stderr, _("Try `%s --help' for more information.\n"),
256              program_name);
257   else
258     {
259       printf (_("\
260 Usage: %s [OPTION]\n\
261 Manipulate a tape drive, accepting commands from a remote process.\n\
262 \n\
263   --version  Output version info.\n\
264   --help  Output this help.\n"),
265               program_name);
266       fputs (_("\nReport bugs to <bug-tar@gnu.org>.\n"), stdout);
267     }
268
269   exit (status);
270 }
271
272 int
273 main (int argc, char *const *argv)
274 {
275   char command;
276   ssize_t status;
277
278   /* FIXME: Localization is meaningless, unless --help and --version are
279      locally used.  Localization would be best accomplished by the calling
280      tar, on messages found within error packets.  */
281
282   program_name = argv[0];
283   setlocale (LC_ALL, "");
284   bindtextdomain (PACKAGE, LOCALEDIR);
285   textdomain (PACKAGE);
286
287   switch (getopt_long (argc, argv, "", long_opts, NULL))
288     {
289     default:
290       usage (EXIT_FAILURE);
291       
292     case 'h':
293       usage (EXIT_SUCCESS);
294       
295     case 'v':
296       {
297         printf ("rmt (GNU %s) %s\n", PACKAGE, VERSION);
298         print_copyright ("2001 Free Software Foundation, Inc.");
299         puts (_("\
300 This program comes with NO WARRANTY, to the extent permitted by law.\n\
301 You may redistribute it under the terms of the GNU General Public License;\n\
302 see the file named COPYING for details."));
303       }
304       return EXIT_SUCCESS;
305
306     case -1:
307       break;
308     }
309
310   if (optind < argc)
311     {
312       if (optind != argc - 1)
313         usage (EXIT_FAILURE);
314       debug_file = fopen (argv[optind], "w");
315       if (debug_file == 0)
316         {
317           report_numbered_error (errno);
318           exit (EXIT_FAILURE);
319         }
320       setbuf (debug_file, 0);
321     }
322
323 top:
324   errno = 0;
325   status = 0;
326   if (safe_read (STDIN_FILENO, &command, 1) != 1)
327     return EXIT_SUCCESS;
328
329   switch (command)
330     {
331       /* FIXME: Maybe 'H' and 'V' for --help and --version output?  */
332
333     case 'O':
334       {
335         char device_string[STRING_SIZE];
336         char oflag_string[STRING_SIZE];
337
338         get_string (device_string);
339         get_string (oflag_string);
340         DEBUG2 ("rmtd: O %s %s\n", device_string, oflag_string);
341
342         if (tape >= 0)
343           close (tape);
344
345         tape = open (device_string, decode_oflag (oflag_string), MODE_RW);
346         if (tape < 0)
347           goto ioerror;
348         goto respond;
349       }
350
351     case 'C':
352       {
353         char device_string[STRING_SIZE];
354
355         get_string (device_string); /* discard */
356         DEBUG ("rmtd: C\n");
357
358         if (close (tape) < 0)
359           goto ioerror;
360         tape = -1;
361         goto respond;
362       }
363
364     case 'L':
365       {
366         char count_string[STRING_SIZE];
367         char position_string[STRING_SIZE];
368         off_t count = 0;
369         int negative;
370         int whence;
371         char *p;
372
373         get_string (count_string);
374         get_string (position_string);
375         DEBUG2 ("rmtd: L %s %s\n", count_string, position_string);
376
377         /* Parse count_string, taking care to check for overflow.
378            We can't use standard functions,
379            since off_t might be longer than long.  */
380
381         for (p = count_string;  *p == ' ' || *p == '\t';  p++)
382           continue;
383
384         negative = *p == '-';
385         p += negative || *p == '+';
386
387         for (;;)
388           {
389             int digit = *p++ - '0';
390             if (9 < (unsigned) digit)
391               break;
392             else
393               {
394                 off_t c10 = 10 * count;
395                 off_t nc = negative ? c10 - digit : c10 + digit;
396                 if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
397                   {
398                     report_error_message (N_("Seek offset out of range"));
399                     exit (EXIT_FAILURE);
400                   }
401                 count = nc;
402               }
403           }
404
405         switch (atoi (position_string))
406           {
407           case 0: whence = SEEK_SET; break;
408           case 1: whence = SEEK_CUR; break;
409           case 2: whence = SEEK_END; break;
410           default:
411             report_error_message (N_("Seek direction out of range"));
412             exit (EXIT_FAILURE);
413           }
414         count = lseek (tape, count, whence);
415         if (count < 0)
416           goto ioerror;
417
418         /* Convert count back to string for reply.
419            We can't use sprintf, since off_t might be longer than long.  */
420         p = count_string + sizeof count_string;
421         *--p = '\0';
422         do
423           *--p = '0' + (int) (count % 10);
424         while ((count /= 10) != 0);
425         
426         DEBUG1 ("rmtd: A %s\n", p);
427
428         sprintf (reply_buffer, "A%s\n", p);
429         full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
430         goto top;
431       }
432
433     case 'W':
434       {
435         char count_string[STRING_SIZE];
436         size_t size;
437         size_t counter;
438
439         get_string (count_string);
440         size = atol (count_string);
441         DEBUG1 ("rmtd: W %s\n", count_string);
442
443         prepare_record_buffer (size);
444         for (counter = 0; counter < size; counter += status)
445           {
446             status = safe_read (STDIN_FILENO, &record_buffer[counter],
447                                 size - counter);
448             if (status <= 0)
449               {
450                 DEBUG (_("rmtd: Premature eof\n"));
451
452                 report_error_message (N_("Premature end of file"));
453                 exit (EXIT_FAILURE); /* exit status used to be 2 */
454               }
455           }
456         status = full_write (tape, record_buffer, size);
457         if (status < 0)
458           goto ioerror;
459         goto respond;
460       }
461
462     case 'R':
463       {
464         char count_string[STRING_SIZE];
465         size_t size;
466
467         get_string (count_string);
468         DEBUG1 ("rmtd: R %s\n", count_string);
469
470         size = atol (count_string);
471         prepare_record_buffer (size);
472         status = safe_read (tape, record_buffer, size);
473         if (status < 0)
474           goto ioerror;
475         sprintf (reply_buffer, "A%ld\n", (long) status);
476         full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
477         full_write (STDOUT_FILENO, record_buffer, status);
478         goto top;
479       }
480
481     case 'I':
482       {
483         char operation_string[STRING_SIZE];
484         char count_string[STRING_SIZE];
485
486         get_string (operation_string);
487         get_string  (count_string);
488         DEBUG2 ("rmtd: I %s %s\n", operation_string, count_string);
489
490 #ifdef MTIOCTOP
491         {
492           struct mtop mtop;
493           const char *p;
494           off_t count = 0;
495           int negative;
496
497           /* Parse count_string, taking care to check for overflow.
498              We can't use standard functions,
499              since off_t might be longer than long.  */
500           
501           for (p = count_string;  *p == ' ' || *p == '\t';  p++)
502             continue;
503           
504           negative = *p == '-';
505           p += negative || *p == '+';
506           
507           for (;;)
508             {
509               int digit = *p++ - '0';
510               if (9 < (unsigned) digit)
511                 break;
512               else
513                 {
514                   off_t c10 = 10 * count;
515                   off_t nc = negative ? c10 - digit : c10 + digit;
516                   if (c10 / 10 != count || (negative ? c10 < nc : nc < c10))
517                     {
518                       report_error_message (N_("Seek offset out of range"));
519                       exit (EXIT_FAILURE);
520                     }
521                   count = nc;
522                 }
523             }
524
525           mtop.mt_count = count;
526           if (mtop.mt_count != count)
527             {
528               report_error_message (N_("Seek offset out of range"));
529               exit (EXIT_FAILURE);
530             }
531           mtop.mt_op = atoi (operation_string);
532
533           if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
534             goto ioerror;
535         }
536 #endif
537         goto respond;
538       }
539
540     case 'S':                   /* status */
541       {
542         DEBUG ("rmtd: S\n");
543
544 #ifdef MTIOCGET
545         {
546           struct mtget operation;
547
548           if (ioctl (tape, MTIOCGET, (char *) &operation) < 0)
549             goto ioerror;
550           status = sizeof operation;
551           sprintf (reply_buffer, "A%ld\n", (long) status);
552           full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
553           full_write (STDOUT_FILENO, (char *) &operation, sizeof operation);
554         }
555 #endif
556         goto top;
557       }
558
559     default:
560       DEBUG1 (_("rmtd: Garbage command %c\n"), command);
561
562       report_error_message (N_("Garbage command"));
563       exit (EXIT_FAILURE);      /* exit status used to be 3 */
564     }
565
566 respond:
567   DEBUG1 ("rmtd: A %ld\n", (long) status);
568
569   sprintf (reply_buffer, "A%ld\n", (long) status);
570   full_write (STDOUT_FILENO, reply_buffer, strlen (reply_buffer));
571   goto top;
572
573 ioerror:
574   report_numbered_error (errno);
575   goto top;
576 }