]> Dogcows Code - chaz/tar/blobdiff - src/update.c
Update copyright years.
[chaz/tar] / src / update.c
index 19e98a7d1b5ce05f3ee05fd1a058b6f82ce050c6..e9fad3f2ae48ab6a196c9b6aa85d4a76da9192ad 100644 (file)
 /* Update a tar archive.
-   Copyright (C) 1988 Free Software Foundation
-
-This file is part of GNU Tar.
-
-GNU Tar is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Tar is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Tar; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
-
-/* JF implement the 'r' 'u' and 'A' options for tar. */
-/* The 'A' option is my own invention:  It means that the file-names are
-   tar files, and they should simply be appended to the end of the archive.
-   No attempt is made to block the reads from the args; if they're on raw
-   tape or something like that, it'll probably lose. . . */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <errno.h>
-#ifndef STDC_HEADERS
-extern int errno;
-#endif
-
-#ifndef NO_MTIO
-#include <sys/ioctl.h>
-#include <sys/mtio.h>
-#endif
-
-#ifdef BSD42
-#include <sys/file.h>
-#else
-#ifndef V7
-#include <fcntl.h>
-#endif
-#endif
-
-#ifndef        __MSDOS__
-#include <pwd.h>
-#include <grp.h>
-#endif
-
-#define STDIN 0
-#define STDOUT 1
-
-#include "tar.h"
-#include "port.h"
-#include "rmt.h"
-
-int time_to_start_writing = 0; /* We've hit the end of the old stuff,
-                                  and its time to start writing new stuff
-                                  to the tape.  This involves seeking
-                                  back one block and re-writing the current
-                                  block (which has been changed). */
-
-char *output_start;            /* Pointer to where we started to write in
-                                  the first block we write out.  This is used
-                                  if we can't backspace the output and have
-                                  to null out the first part of the block */
-
-extern void skip_file();
-extern void skip_extended_headers();
-
-extern union record *head;
-extern struct stat hstat;
-
-void append_file();
-void close_archive();
-int confirm();
-void decode_header();
-void fl_read();
-void fl_write();
-void flush_archive();
-int move_arch();
-struct name *name_scan();
-char   *name_from_list();
-void name_expand();
-void name_gather();
-void names_notfound();
-void open_archive();
-int read_header();
-void reset_eof();
-void write_block();
-void write_eot();
-
-/* Implement the 'r' (add files to end of archive), and 'u' (add files to
-   end of archive if they arent there, or are more up to date than the
-   version in the archive.) commands.*/
-void
-update_archive()
-{
-       int     found_end = 0;
-       int     status = 3;
-       int     prev_status;
-       char    *p;
-       struct name *name;
-       extern void dump_file();
-
-       name_gather();
-       if(cmd_mode==CMD_UPDATE)
-               name_expand();
-       open_archive(2);        /* Open for updating */
-
-       do {
-               prev_status=status;
-               status=read_header();
-               switch(status) {
-               case EOF:
-                       found_end=1;
-                       break;
-
-               case 0:         /* A bad record */
-                       userec(head);
-                       switch(prev_status) {
-                       case 3:
-                               msg("This doesn't look like a tar archive.");
-                               /* FALL THROUGH */
-                       case 2:
-                       case 1:
-                               msg("Skipping to next header");
-                       case 0:
-                               break;
-                       }
-                       break;
-
-                       /* A good record */
-               case 1:
- /* printf("File %s\n",head->header.name); */
-                       /* head->header.name[NAMSIZ-1]='\0'; */
-                       if(cmd_mode==CMD_UPDATE && (name=name_scan(head->header.name))) {
-                               /* struct stat hstat; */
-                               struct stat nstat;
-                               int head_standard;
-
-                               decode_header(head,&hstat,&head_standard,0);
-                               if(stat(head->header.name,&nstat)<0) {
-                                       msg_perror("can't stat %s:",head->header.name);
-                               } else {
-                                       if(hstat.st_mtime>=nstat.st_mtime)
-                                               name->found++;
-                               }
-                       }
-                       userec(head);
-                       if (head->header.isextended)
-                               skip_extended_headers();
-                       skip_file((long)hstat.st_size);
-                       break;
-
-               case 2:
-                       ar_record=head;
-                       found_end = 1;
-                       break;
-               }
-       } while(!found_end);
-
-       reset_eof();
-       time_to_start_writing = 1;
-       output_start=ar_record->charptr;
-
-       while(p=name_from_list()) {
-               if(f_confirm && !confirm("add", p))
-                       continue;
-               if(cmd_mode==CMD_CAT)
-                       append_file(p);
-               else
-                       dump_file(p,-1);
-       }
-
-       write_eot();
-       close_archive();
-       names_notfound();
-}
-
-/* Catenate file p to the archive without creating a header for it.  It had
-   better be a tar file or the archive is screwed */
-
-void
-append_file(p)
-char *p;
-{
-       int     fd;
-       struct stat statbuf;
-       long    bytes_left;
-       union record *start;
-       long    bufsiz,count;
-
-       if(0 != stat(p,&statbuf) || (fd=open(p,O_RDONLY|O_BINARY))<0) {
-               msg_perror("can't open file %s",p);
-               errors++;
-               return;
-       }
-
-       bytes_left = statbuf.st_size;
-
-       while(bytes_left>0) {
-               start=findrec();
-               bufsiz=endofrecs()->charptr - start->charptr;
-               if(bytes_left < bufsiz) {
-                       bufsiz = bytes_left;
-                       count = bufsiz % RECORDSIZE;
-                       if(count)
-                               bzero(start->charptr + bytes_left,(int)(RECORDSIZE-count));
-               }
-               count=read(fd,start->charptr,bufsiz);
-               if(count<0) {
-                       msg_perror("read error at byte %ld reading %d bytes in file %s",statbuf.st_size-bytes_left,bufsiz,p);
-                       exit(EX_ARGSBAD);               /* FOO */
-               }
-               bytes_left-=count;
-               userec(start+(count-1)/RECORDSIZE);
-               if(count!=bufsiz) {
-                       msg("%s: file shrunk by %d bytes, yark!",p,bytes_left);
-                       abort();
-               }
-       }
-       (void)close(fd);
-}
-
-#ifdef DONTDEF
-bprint(fp,buf,num)
-FILE *fp;
-char *buf;
-{
-       int     c;
-
-       if(num==0 || num==-1)
-               return;
-       fputs(" '",fp);
-       while(num--) {
-               c= *buf++;
-               if(c=='\\') fputs("\\\\",fp);
-               else if(c>=' ' && c<='~')
-                       putc(c,fp);
-               else switch(c) {
-               case '\n':
-                       fputs("\\n",fp);
-                       break;
-               case '\r':
-                       fputs("\\r",fp);
-                       break;
-               case '\b':
-                       fputs("\\b",fp);
-                       break;
-               case '\0':
-                       /* fputs("\\-",fp); */
-                       break;
-               default:
-                       fprintf(fp,"\\%03o",c);
-                       break;
-               }
-       }
-       fputs("'\n",fp);
-}
-#endif
-
-int number_of_blocks_read = 0;
-
-int number_of_new_records = 0;
-int number_of_records_needed = 0;
 
-union record *new_block = 0;
-union record *save_block = 0;
-
-void
-junk_archive()
+   Copyright 1988, 1992, 1994, 1996-1997, 1999-2001, 2003-2005, 2007,
+   2010, 2013-2014 Free Software Foundation, Inc.
+
+   This file is part of GNU tar.
+
+   GNU tar is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   GNU tar is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Implement the 'r', 'u' and 'A' options for tar.  'A' means that the
+   file names are tar files, and they should simply be appended to the end
+   of the archive.  No attempt is made to record the reads from the args; if
+   they're on raw tape or something like that, it'll probably lose...  */
+
+#include <system.h>
+#include <quotearg.h>
+#include "common.h"
+
+/* FIXME: This module should not directly handle the following variable,
+   instead, this should be done in buffer.c only.  */
+extern union block *current_block;
+
+/* We've hit the end of the old stuff, and its time to start writing new
+   stuff to the tape.  This involves seeking back one record and
+   re-writing the current record (which has been changed).
+   FIXME: Either eliminate it or move it to common.h.
+*/
+bool time_to_start_writing;
+
+/* Pointer to where we started to write in the first record we write out.
+   This is used if we can't backspace the output and have to null out the
+   first part of the record.  */
+char *output_start;
+
+/* Catenate file FILE_NAME to the archive without creating a header for it.
+   It had better be a tar file or the archive is screwed.  */
+static void
+append_file (char *file_name)
 {
-       int     found_stuff = 0;
-       int     status = 3;
-       int     prev_status;
-       struct name *name;
-
-       /* int dummy_head; */
-       int number_of_records_to_skip = 0;
-       int number_of_records_to_keep = 0;
-       int number_of_kept_records_in_block;
-       int sub_status;
-       extern int write_archive_to_stdout;
-
-/* fprintf(stderr,"Junk files\n"); */
-       name_gather();
-       open_archive(2);
-
-       while(!found_stuff) {
-               prev_status=status;
-               status=read_header();
-               switch(status) {
-               case EOF:
-                       found_stuff = 1;
-                       break;
-
-               case 0:
-                       userec(head);
-                       switch(prev_status) {
-                       case 3:
-                               msg("This doesn't look like a tar archive.");
-                               /* FALL THROUGH */
-                       case 2:
-                       case 1:
-                               msg("Skipping to next header");
-                               /* FALL THROUGH */
-                       case 0:
-                               break;
-                       }
-                       break;
-
-               case 1:
-                       /* head->header.name[NAMSIZ-1] = '\0'; */
- /* fprintf(stderr,"file %s\n",head->header.name); */
-                       if((name=name_scan(head->header.name))==(struct name *)0) {
-                               userec(head);
- /* fprintf(stderr,"Skip %ld\n",(long)(hstat.st_size)); */
-                               if (head->header.isextended)
-                                       skip_extended_headers();
-                               skip_file((long)(hstat.st_size));
-                               break;
-                       }
-                       name->found = 1;
-                       found_stuff = 2;
-                       break;
-
-               case 2:
-                       found_stuff = 1;
-                       break;
-               }
-       }
- /* fprintf(stderr,"Out of first loop\n"); */
-
-       if(found_stuff!=2) {
-               write_eot();
-               close_archive();
-               names_notfound();
-               return;
-       }
-
-       if(write_archive_to_stdout)
-               write_archive_to_stdout = 0;
-       new_block = (union record *)malloc(blocksize);
-       if(new_block==0) {
-               msg("Can't allocate secondary block of %d bytes",blocksize);
-               exit(EX_SYSTEM);
-       }
-
-               /* Save away records before this one in this block */
-       number_of_new_records=ar_record-ar_block;
-       number_of_records_needed = blocking - number_of_new_records;
-       if(number_of_new_records)
-               bcopy((void *)ar_block,(void *)new_block,(number_of_new_records)*RECORDSIZE);
-
- /* fprintf(stderr,"Saved %d recs, need %d more\n",number_of_new_records,number_of_records_needed); */
-       userec(head);
-       if (head->header.isextended)
-               skip_extended_headers();
-       skip_file((long)(hstat.st_size));
-       found_stuff=0;
-       /* goto flush_file; */
-
-       for(;;) {
-                       /* Fill in a block */
-       /* another_file: */
-               if(ar_record==ar_last) {
- /* fprintf(stderr,"New block\n"); */
-                       flush_archive();
-                       number_of_blocks_read++;
-               }
-               sub_status = read_header();
- /* fprintf(stderr,"Header type %d\n",sub_status); */
-
-               if(sub_status==2 && f_ignorez) {
-                       userec(head);
-                       continue;
-               }
-               if(sub_status==EOF || sub_status==2) {
-                       found_stuff = 1;
-                       bzero(new_block[number_of_new_records].charptr,RECORDSIZE*number_of_records_needed);
-                       number_of_new_records+=number_of_records_needed;
-                       number_of_records_needed = 0;
-                       write_block(0);
-                       break;
-               }
-
-               if(sub_status==0) {
-                       msg("Deleting non-header from archive.");
-                       userec(head);
-                       continue;
-               }
-
-               /* Found another header.  Yipee! */
-               /* head->header.name[NAMSIZ-1] = '\0'; */
- /* fprintf(stderr,"File %s ",head->header.name); */
-               if(name=name_scan(head->header.name)) {
-                       name->found = 1;
- /* fprintf(stderr,"Flush it\n"); */
-               /* flush_file: */
-                       /* decode_header(head,&hstat,&dummy_head,0); */
-                       userec(head);
-                       number_of_records_to_skip=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE;
- /* fprintf(stderr,"Flushing %d recs from %s\n",number_of_records_to_skip,head->header.name); */
-
-                       while(ar_last-ar_record<=number_of_records_to_skip) {
-
- /* fprintf(stderr,"Block: %d <= %d  ",ar_last-ar_record,number_of_records_to_skip); */
-                               number_of_records_to_skip -= (ar_last - ar_record);
-                               flush_archive();
-                               number_of_blocks_read++;
- /* fprintf(stderr,"Block %d left\n",number_of_records_to_skip); */
-                       }
-                       ar_record+=number_of_records_to_skip;
- /* fprintf(stderr,"Final %d\n",number_of_records_to_skip); */
-                       number_of_records_to_skip = 0;
-                       continue;
-               }
-
-       /* copy_header: */
-               new_block[number_of_new_records]= *head;
-               number_of_new_records++;
-               number_of_records_needed--;
-               number_of_records_to_keep=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE;
-               userec(head);
-               if(number_of_records_needed==0)
-                       write_block(1);
-       /* copy_data: */
-               number_of_kept_records_in_block = ar_last - ar_record;
-               if(number_of_kept_records_in_block > number_of_records_to_keep)
-                       number_of_kept_records_in_block = number_of_records_to_keep;
-
- /* fprintf(stderr,"Need %d kept_in %d keep %d\n",blocking,number_of_kept_records_in_block,number_of_records_to_keep); */
-
-               while(number_of_records_to_keep) {
-                       int n;
-
-                       if(ar_record==ar_last) {
- /* fprintf(stderr,"Flush. . .\n"); */
-                               fl_read();
-                               number_of_blocks_read++;
-                               ar_record=ar_block;
-                               number_of_kept_records_in_block = blocking;
-                               if(number_of_kept_records_in_block > number_of_records_to_keep)
-                                       number_of_kept_records_in_block = number_of_records_to_keep;
-                       }
-                       n = number_of_kept_records_in_block;
-                       if(n>number_of_records_needed)
-                               n = number_of_records_needed;
-
- /* fprintf(stderr,"Copying %d\n",n); */
-                       bcopy((void *)ar_record, (void *)(new_block+number_of_new_records), n*RECORDSIZE);
-                       number_of_new_records           += n;
-                       number_of_records_needed        -= n;
-                       ar_record                       += n;
-                       number_of_records_to_keep       -= n;
-                       number_of_kept_records_in_block -= n;
- /* fprintf(stderr,"Now new %d  need %d  keep %d  keep_in %d rec %d/%d\n", 
- number_of_new_records,number_of_records_needed,number_of_records_to_keep,
- number_of_kept_records_in_block,ar_record-ar_block,ar_last-ar_block); */
-                       if(number_of_records_needed == 0) {
-                               write_block(1);
-                       }
-               }
+  int handle = openat (chdir_fd, file_name, O_RDONLY | O_BINARY);
+  struct stat stat_data;
+
+  if (handle < 0)
+    {
+      open_error (file_name);
+      return;
+    }
+
+  if (fstat (handle, &stat_data) != 0)
+    stat_error (file_name);
+  else
+    {
+      off_t bytes_left = stat_data.st_size;
+
+      while (bytes_left > 0)
+       {
+         union block *start = find_next_block ();
+         size_t buffer_size = available_space_after (start);
+         size_t status;
+         char buf[UINTMAX_STRSIZE_BOUND];
+
+         if (bytes_left < buffer_size)
+           {
+             buffer_size = bytes_left;
+             status = buffer_size % BLOCKSIZE;
+             if (status)
+               memset (start->buffer + bytes_left, 0, BLOCKSIZE - status);
+           }
+
+         status = safe_read (handle, start->buffer, buffer_size);
+         if (status == SAFE_READ_ERROR)
+           read_fatal_details (file_name, stat_data.st_size - bytes_left,
+                               buffer_size);
+         if (status == 0)
+           FATAL_ERROR ((0, 0,
+                         ngettext ("%s: File shrank by %s byte",
+                                   "%s: File shrank by %s bytes",
+                                   bytes_left),
+                         quotearg_colon (file_name),
+                         STRINGIFY_BIGINT (bytes_left, buf)));
+
+         bytes_left -= status;
+
+         set_next_block_after (start + (status - 1) / BLOCKSIZE);
        }
+    }
 
-       write_eot();
-       close_archive();
-       names_notfound();
+  if (close (handle) != 0)
+    close_error (file_name);
 }
 
+/* Implement the 'r' (add files to end of archive), and 'u' (add files
+   to end of archive if they aren't there, or are more up to date than
+   the version in the archive) commands.  */
 void
-write_block(f)
-     int f;
+update_archive (void)
 {
- /* fprintf(stderr,"Write block\n"); */
-       /* We've filled out a block.  Write it out. */
-
-       /* Backspace back to where we started. . . */
-       if(archive!=STDIN)
-               (void)move_arch(-(number_of_blocks_read+1));
-
-       save_block = ar_block;
-       ar_block = new_block;
-
-       if(archive==STDIN)
-               archive=STDOUT;
-       fl_write();
-
-       if(archive==STDOUT)
-               archive=STDIN;
-       ar_block = save_block;
-
-       if(f) {
-               /* Move the tape head back to where we were */
-               if(archive!=STDIN)
-                       (void)move_arch(number_of_blocks_read);
-               number_of_blocks_read--;
+  enum read_header previous_status = HEADER_STILL_UNREAD;
+  bool found_end = false;
+
+  name_gather ();
+  open_archive (ACCESS_UPDATE);
+  buffer_write_global_xheader ();
+
+  while (!found_end)
+    {
+      enum read_header status = read_header (&current_header,
+                                             &current_stat_info,
+                                             read_header_auto);
+
+      switch (status)
+       {
+       case HEADER_STILL_UNREAD:
+       case HEADER_SUCCESS_EXTENDED:
+         abort ();
+
+       case HEADER_SUCCESS:
+         {
+           struct name *name;
+
+           decode_header (current_header, &current_stat_info,
+                          &current_format, 0);
+           transform_stat_info (current_header->header.typeflag,
+                                &current_stat_info);
+           archive_format = current_format;
+
+           if (subcommand_option == UPDATE_SUBCOMMAND
+               && (name = name_scan (current_stat_info.file_name)) != NULL)
+             {
+               struct stat s;
+
+               chdir_do (name->change_dir);
+               if (deref_stat (current_stat_info.file_name, &s) == 0)
+                 {
+                   if (S_ISDIR (s.st_mode))
+                     {
+                       char *p, *dirp = tar_savedir (name->name, 1);
+                       if (dirp)
+                         {
+                           namebuf_t nbuf = namebuf_create (name->name);
+
+                           for (p = dirp; *p; p += strlen (p) + 1)
+                             addname (namebuf_name (nbuf, p),
+                                      0, false, NULL);
+
+                           namebuf_free (nbuf);
+                           free (dirp);
+
+                           remname (name);
+                         }
+                     }
+                   else if (tar_timespec_cmp (get_stat_mtime (&s),
+                                              current_stat_info.mtime)
+                            <= 0)
+                     remname (name);
+                 }
+             }
+
+           skip_member ();
+           break;
+         }
+
+       case HEADER_ZERO_BLOCK:
+         current_block = current_header;
+         found_end = true;
+         break;
+
+       case HEADER_END_OF_FILE:
+         found_end = true;
+         break;
+
+       case HEADER_FAILURE:
+         set_next_block_after (current_header);
+         switch (previous_status)
+           {
+           case HEADER_STILL_UNREAD:
+             WARN ((0, 0, _("This does not look like a tar archive")));
+             /* Fall through.  */
+
+           case HEADER_SUCCESS:
+           case HEADER_ZERO_BLOCK:
+             ERROR ((0, 0, _("Skipping to next header")));
+             /* Fall through.  */
+
+           case HEADER_FAILURE:
+             break;
+
+           case HEADER_END_OF_FILE:
+           case HEADER_SUCCESS_EXTENDED:
+             abort ();
+           }
+         break;
        }
 
-       number_of_records_needed = blocking;
-       number_of_new_records = 0;
+      tar_stat_destroy (&current_stat_info);
+      previous_status = status;
+    }
+
+  reset_eof ();
+  time_to_start_writing = true;
+  output_start = current_block->buffer;
+
+  {
+    struct name const *p;
+    while ((p = name_from_list ()) != NULL)
+      {
+       char *file_name = p->name;
+       if (excluded_name (file_name))
+         continue;
+       if (interactive_option && !confirm ("add", file_name))
+         continue;
+       if (subcommand_option == CAT_SUBCOMMAND)
+         append_file (file_name);
+       else
+         dump_file (0, file_name, file_name);
+      }
+  }
+
+  write_eot ();
+  close_archive ();
+  finish_deferred_unlinks ();
+  names_notfound ();
 }
-
-/* Move archive descriptor by n blocks worth.  If n is positive we move
-   forward, else we move negative.   If its a tape, MTIOCTOP had better
-   work.  If its something else, we try to seek on it.  If we can't
-   seek, we lose! */
-int
-move_arch(n)
-     int n;
-{
-       long cur;
-
-#ifdef MTIOCTOP
-       struct mtop t;
-       int er;
-
-       if(n>0) {
-               t.mt_op = MTFSR;
-               t.mt_count = n;
-       } else {
-               t.mt_op = MTBSR;
-               t.mt_count = -n;
-       }
-       if((er=rmtioctl(archive,MTIOCTOP,&t))>=0)
-               return 1;
-       if(errno==EIO && (er=rmtioctl(archive,MTIOCTOP,&t))>=0)
-               return 1;
-#endif
-
-       cur=rmtlseek(archive,0L,1);
-       cur+=blocksize*n;
-
- /* fprintf(stderr,"Fore to %x\n",cur); */
-       if(rmtlseek(archive,cur,0)!=cur) {
-               /* Lseek failed.  Try a different method */
-               msg("Couldn't re-position archive file.");
-               exit(EX_BADARCH);
-       }
-       return 3;
-}
-
This page took 0.039123 seconds and 4 git commands to generate.