Fix some C compatibility bugs reported by Joerg Schilling.
[debian/tar] / src / system.c
1 /* System-dependent calls for tar.
2
3    Copyright (C) 2003 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by the
7    Free Software Foundation; either version 2, or (at your option) any later
8    version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
13    Public License for more details.
14
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, write to the Free Software Foundation, Inc.,
17    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 #include "system.h"
20
21 #include "common.h"
22 #include "rmt.h"
23 #include <signal.h>
24
25 #if MSDOS
26
27 bool
28 sys_get_archive_stat ()
29 {
30   return 0;
31 }
32
33 bool
34 sys_file_is_archive (struct tar_stat_info *p)
35 {
36   return false;
37 }
38
39 void
40 sys_save_archive_dev_ino ()
41 {
42 }
43
44 void
45 sys_detect_dev_null_output ()
46 {
47   static char const dev_null[] = "nul";
48
49   dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
50                      || (! _isrmt (archive)));
51 }
52
53 void
54 sys_drain_input_pipe ()
55 {
56 }
57
58 void
59 sys_wait_for_child (pid_t child_pid)
60 {
61 }
62
63 void
64 sys_spawn_shell ()
65 {
66   spawnl (P_WAIT, getenv ("COMSPEC"), "-", 0);
67 }
68
69 void
70 sys_compare_uid_gid (struct stat *a, struct stat *b)
71 {
72   /* stat() in djgpp's C library gives a constant number of 42 as the
73      uid and gid of a file.  So, comparing an FTP'ed archive just after
74      unpack would fail on MSDOS.  */
75 }
76
77 void
78 sys_compare_links (struct stat *link_data, struct stat *stat_data)
79 {
80 }
81
82 int
83 sys_truncate (int fd)
84 {
85   return write (fd, "", 0);
86 }
87
88 void
89 sys_reset_uid_gid ()
90 {
91 }
92
93 ssize_t
94 sys_write_archive_buffer (void)
95 {
96   ssize_t status;
97   ssize_t written = 0;
98
99   while (0 <= (status = full_write (archive, record_start->buffer + written,
100                                     record_size - written)))
101     {
102       written += status;
103       if (written == record_size)
104         break;
105     }
106
107   return written ? written : status;
108 }
109
110 /* Set ARCHIVE for writing, then compressing an archive.  */
111 void
112 sys_child_open_for_compress (void)
113 {
114   FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
115 }
116
117 /* Set ARCHIVE for uncompressing, then reading an archive.  */
118 void
119 sys_child_open_for_uncompress (void)
120 {
121   FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
122 }
123
124 #else
125
126 extern union block *record_start; /* FIXME */
127
128 static struct stat archive_stat; /* stat block for archive file */
129
130 bool
131 sys_get_archive_stat ()
132 {
133   return fstat (archive, &archive_stat) == 0;
134 }
135     
136 bool
137 sys_file_is_archive (struct tar_stat_info *p)
138 {
139   return (ar_dev && p->stat.st_dev == ar_dev && p->stat.st_ino == ar_ino);
140 }
141
142 /* Save archive file inode and device numbers */
143 void
144 sys_save_archive_dev_ino ()
145 {
146   if (!_isrmt (archive) && S_ISREG (archive_stat.st_mode))
147     {
148       ar_dev = archive_stat.st_dev;
149       ar_ino = archive_stat.st_ino;
150     }
151   else
152     ar_dev = 0;
153 }
154
155 /* Detect if outputting to "/dev/null".  */
156 void
157 sys_detect_dev_null_output ()
158 {
159   static char const dev_null[] = "/dev/null";
160   struct stat dev_null_stat;
161
162   dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
163                      || (! _isrmt (archive)
164                          && S_ISCHR (archive_stat.st_mode)
165                          && stat (dev_null, &dev_null_stat) == 0
166                          && archive_stat.st_dev == dev_null_stat.st_dev
167                          && archive_stat.st_ino == dev_null_stat.st_ino));
168 }
169
170 /* Manage to fully drain a pipe we might be reading, so to not break it on
171    the producer after the EOF block.  FIXME: one of these days, GNU tar
172    might become clever enough to just stop working, once there is no more
173    work to do, we might have to revise this area in such time.  */
174
175 void
176 sys_drain_input_pipe ()
177 {
178   if (access_mode == ACCESS_READ
179       && ! _isrmt (archive)
180       && (S_ISFIFO (archive_stat.st_mode) || S_ISSOCK (archive_stat.st_mode)))
181     while (rmtread (archive, record_start->buffer, record_size) > 0)
182       continue;
183 }
184
185 void
186 sys_wait_for_child (pid_t child_pid)
187 {
188   if (child_pid)
189     {
190       int wait_status;
191
192       while (waitpid (child_pid, &wait_status, 0) == -1)
193         if (errno != EINTR)
194           {
195             waitpid_error (use_compress_program_option);
196             break;
197           }
198
199       if (WIFSIGNALED (wait_status))
200         ERROR ((0, 0, _("Child died with signal %d"),
201                 WTERMSIG (wait_status)));
202       else if (WEXITSTATUS (wait_status) != 0)
203         ERROR ((0, 0, _("Child returned status %d"),
204                 WEXITSTATUS (wait_status)));
205     }
206 }
207
208 void
209 sys_spawn_shell ()
210 {
211   pid_t child;
212   const char *shell = getenv ("SHELL");
213   if (! shell)
214     shell = "/bin/sh";
215   child = xfork ();
216   if (child == 0)
217     {
218       execlp (shell, "-sh", "-i", (char *) 0);
219       exec_fatal (shell);
220     }
221   else
222     {
223       int wait_status;
224       while (waitpid (child, &wait_status, 0) == -1)
225         if (errno != EINTR)
226           {
227             waitpid_error (shell);
228             break;
229           }
230     }
231 }
232
233 void
234 sys_compare_uid_gid (struct stat *a, struct stat *b)
235 {
236   if (a->st_uid != b->st_uid)
237     report_difference (_("Uid differs"));
238   if (a->st_gid != b->st_gid)
239     report_difference (_("Gid differs"));
240 }
241
242 void
243 sys_compare_links (struct stat *link_data, struct stat *stat_data)
244 {
245   if (stat_data->st_dev != link_data->st_dev
246       || stat_data->st_ino != link_data->st_ino)
247     {
248       report_difference (_("Not linked to %s"),
249                          quote (current_stat_info.link_name));
250     }
251 }
252
253 int
254 sys_truncate (int fd)
255 {
256   off_t pos = lseek (fd, (off_t) 0, SEEK_CUR);
257   return pos < 0 ? -1 : ftruncate (fd, pos);
258 }
259
260 void
261 sys_reset_uid_gid ()
262 {
263   setuid (getuid ());
264   setgid (getgid ());
265 }
266
267 /* Return nonzero if NAME is the name of a regular file, or if the file
268    does not exist (so it would be created as a regular file).  */
269 static int
270 is_regular_file (const char *name)
271 {
272   struct stat stbuf;
273
274   if (stat (name, &stbuf) == 0)
275     return S_ISREG (stbuf.st_mode);
276   else
277     return errno == ENOENT;
278 }
279
280 ssize_t
281 sys_write_archive_buffer (void)
282 {
283   ssize_t status;
284   ssize_t written = 0;
285
286   while (0 <= (status = rmtwrite (archive, record_start->buffer + written,
287                                   record_size - written)))
288     {
289       written += status;
290       if (written == record_size
291           || _isrmt (archive)
292           || ! (S_ISFIFO (archive_stat.st_mode)
293                 || S_ISSOCK (archive_stat.st_mode)))
294         break;
295     }
296
297   return written ? written : status;
298 }
299
300 #define PREAD 0                 /* read file descriptor from pipe() */
301 #define PWRITE 1                /* write file descriptor from pipe() */
302
303 /* Duplicate file descriptor FROM into becoming INTO.
304    INTO is closed first and has to be the next available slot.  */
305 static void
306 xdup2 (int from, int into)
307 {
308   if (from != into)
309     {
310       int status = close (into);
311
312       if (status != 0 && errno != EBADF)
313         {
314           int e = errno;
315           FATAL_ERROR ((0, e, _("Cannot close")));
316         }
317       status = dup (from);
318       if (status != into)
319         {
320           if (status < 0)
321             {
322               int e = errno;
323               FATAL_ERROR ((0, e, _("Cannot dup")));
324             }
325           abort ();
326         }
327       xclose (from);
328     }
329 }
330
331 /* Set ARCHIVE for writing, then compressing an archive.  */
332 pid_t
333 sys_child_open_for_compress (void)
334 {
335   int parent_pipe[2];
336   int child_pipe[2];
337   pid_t grandchild_pid;
338   pid_t child_pid;
339   int wait_status;
340
341   xpipe (parent_pipe);
342   child_pid = xfork ();
343
344   if (child_pid > 0)
345     {
346       /* The parent tar is still here!  Just clean up.  */
347
348       archive = parent_pipe[PWRITE];
349       xclose (parent_pipe[PREAD]);
350       return child_pid;
351     }
352
353   /* The new born child tar is here!  */
354
355   program_name = _("tar (child)");
356
357   xdup2 (parent_pipe[PREAD], STDIN_FILENO);
358   xclose (parent_pipe[PWRITE]);
359
360   /* Check if we need a grandchild tar.  This happens only if either:
361      a) we are writing stdout: to force reblocking;
362      b) the file is to be accessed by rmt: compressor doesn't know how;
363      c) the file is not a plain file.  */
364
365   if (strcmp (archive_name_array[0], "-") != 0
366       && !_remdev (archive_name_array[0])
367       && is_regular_file (archive_name_array[0]))
368     {
369       if (backup_option)
370         maybe_backup_file (archive_name_array[0], 1);
371
372       /* We don't need a grandchild tar.  Open the archive and launch the
373          compressor.  */
374
375       archive = creat (archive_name_array[0], MODE_RW);
376       if (archive < 0)
377         {
378           int saved_errno = errno;
379
380           if (backup_option)
381             undo_last_backup ();
382           errno = saved_errno;
383           open_fatal (archive_name_array[0]);
384         }
385       xdup2 (archive, STDOUT_FILENO);
386       execlp (use_compress_program_option, use_compress_program_option,
387               (char *) 0);
388       exec_fatal (use_compress_program_option);
389     }
390
391   /* We do need a grandchild tar.  */
392
393   xpipe (child_pipe);
394   grandchild_pid = xfork ();
395
396   if (grandchild_pid == 0)
397     {
398       /* The newborn grandchild tar is here!  Launch the compressor.  */
399
400       program_name = _("tar (grandchild)");
401
402       xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
403       xclose (child_pipe[PREAD]);
404       execlp (use_compress_program_option, use_compress_program_option,
405               (char *) 0);
406       exec_fatal (use_compress_program_option);
407     }
408
409   /* The child tar is still here!  */
410
411   /* Prepare for reblocking the data from the compressor into the archive.  */
412
413   xdup2 (child_pipe[PREAD], STDIN_FILENO);
414   xclose (child_pipe[PWRITE]);
415
416   if (strcmp (archive_name_array[0], "-") == 0)
417     archive = STDOUT_FILENO;
418   else
419     {
420       archive = rmtcreat (archive_name_array[0], MODE_RW, rsh_command_option);
421       if (archive < 0)
422         open_fatal (archive_name_array[0]);
423     }
424
425   /* Let's read out of the stdin pipe and write an archive.  */
426
427   while (1)
428     {
429       ssize_t status = 0;
430       char *cursor;
431       size_t length;
432
433       /* Assemble a record.  */
434
435       for (length = 0, cursor = record_start->buffer;
436            length < record_size;
437            length += status, cursor += status)
438         {
439           size_t size = record_size - length;
440
441           status = safe_read (STDIN_FILENO, cursor, size);
442           if (status <= 0)
443             break;
444         }
445
446       if (status < 0)
447         read_fatal (use_compress_program_option);
448
449       /* Copy the record.  */
450
451       if (status == 0)
452         {
453           /* We hit the end of the file.  Write last record at
454              full length, as the only role of the grandchild is
455              doing proper reblocking.  */
456
457           if (length > 0)
458             {
459               memset (record_start->buffer + length, 0, record_size - length);
460               status = sys_write_archive_buffer ();
461               if (status != record_size)
462                 archive_write_error (status);
463             }
464
465           /* There is nothing else to read, break out.  */
466           break;
467         }
468
469       status = sys_write_archive_buffer ();
470       if (status != record_size)
471         archive_write_error (status);
472     }
473
474   /* Propagate any failure of the grandchild back to the parent.  */
475
476   while (waitpid (grandchild_pid, &wait_status, 0) == -1)
477     if (errno != EINTR)
478       {
479         waitpid_error (use_compress_program_option);
480         break;
481       }
482
483   if (WIFSIGNALED (wait_status))
484     {
485       kill (child_pid, WTERMSIG (wait_status));
486       exit_status = TAREXIT_FAILURE;
487     }
488   else if (WEXITSTATUS (wait_status) != 0)
489     exit_status = WEXITSTATUS (wait_status);
490
491   exit (exit_status);
492 }
493
494 /* Set ARCHIVE for uncompressing, then reading an archive.  */
495 pid_t
496 sys_child_open_for_uncompress (void)
497 {
498   int parent_pipe[2];
499   int child_pipe[2];
500   pid_t grandchild_pid;
501   pid_t child_pid;
502   int wait_status;
503
504   xpipe (parent_pipe);
505   child_pid = xfork ();
506
507   if (child_pid > 0)
508     {
509       /* The parent tar is still here!  Just clean up.  */
510
511       read_full_records_option = 1;
512       archive = parent_pipe[PREAD];
513       xclose (parent_pipe[PWRITE]);
514       return child_pid;
515     }
516
517   /* The newborn child tar is here!  */
518
519   program_name = _("tar (child)");
520
521   xdup2 (parent_pipe[PWRITE], STDOUT_FILENO);
522   xclose (parent_pipe[PREAD]);
523
524   /* Check if we need a grandchild tar.  This happens only if either:
525      a) we're reading stdin: to force unblocking;
526      b) the file is to be accessed by rmt: compressor doesn't know how;
527      c) the file is not a plain file.  */
528
529   if (strcmp (archive_name_array[0], "-") != 0
530       && !_remdev (archive_name_array[0])
531       && is_regular_file (archive_name_array[0]))
532     {
533       /* We don't need a grandchild tar.  Open the archive and lauch the
534          uncompressor.  */
535
536       archive = open (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW);
537       if (archive < 0)
538         open_fatal (archive_name_array[0]);
539       xdup2 (archive, STDIN_FILENO);
540       execlp (use_compress_program_option, use_compress_program_option,
541               "-d", (char *) 0);
542       exec_fatal (use_compress_program_option);
543     }
544
545   /* We do need a grandchild tar.  */
546
547   xpipe (child_pipe);
548   grandchild_pid = xfork ();
549
550   if (grandchild_pid == 0)
551     {
552       /* The newborn grandchild tar is here!  Launch the uncompressor.  */
553
554       program_name = _("tar (grandchild)");
555
556       xdup2 (child_pipe[PREAD], STDIN_FILENO);
557       xclose (child_pipe[PWRITE]);
558       execlp (use_compress_program_option, use_compress_program_option,
559               "-d", (char *) 0);
560       exec_fatal (use_compress_program_option);
561     }
562
563   /* The child tar is still here!  */
564
565   /* Prepare for unblocking the data from the archive into the
566      uncompressor.  */
567
568   xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
569   xclose (child_pipe[PREAD]);
570
571   if (strcmp (archive_name_array[0], "-") == 0)
572     archive = STDIN_FILENO;
573   else
574     archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
575                        MODE_RW, rsh_command_option);
576   if (archive < 0)
577     open_fatal (archive_name_array[0]);
578
579   /* Let's read the archive and pipe it into stdout.  */
580
581   while (1)
582     {
583       char *cursor;
584       size_t maximum;
585       size_t count;
586       ssize_t status;
587
588       clear_read_error_count ();
589
590     error_loop:
591       status = rmtread (archive, record_start->buffer, record_size);
592       if (status < 0)
593         {
594           archive_read_error ();
595           goto error_loop;
596         }
597       if (status == 0)
598         break;
599       cursor = record_start->buffer;
600       maximum = status;
601       while (maximum)
602         {
603           count = maximum < BLOCKSIZE ? maximum : BLOCKSIZE;
604           if (full_write (STDOUT_FILENO, cursor, count) != count)
605             write_error (use_compress_program_option);
606           cursor += count;
607           maximum -= count;
608         }
609     }
610
611   xclose (STDOUT_FILENO);
612
613   /* Propagate any failure of the grandchild back to the parent.  */
614
615   while (waitpid (grandchild_pid, &wait_status, 0) == -1)
616     if (errno != EINTR)
617       {
618         waitpid_error (use_compress_program_option);
619         break;
620       }
621
622   if (WIFSIGNALED (wait_status))
623     {
624       kill (child_pid, WTERMSIG (wait_status));
625       exit_status = TAREXIT_FAILURE;
626     }
627   else if (WEXITSTATUS (wait_status) != 0)
628     exit_status = WEXITSTATUS (wait_status);
629
630   exit (exit_status);
631 }
632
633 #endif /* not MSDOS */
634