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