>Number:         154407
>Category:       bin
>Synopsis:       usr.bin/tar/ ignores error codes from read() just silently 
>pads nulls
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jan 31 04:00:21 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     "Julian H. Stacey" <j...@berklix.com>
>Release:        FreeBSD 8.1-RELEASE amd64
>Organization:
http://berklix.com BSD Linux Unix Consultancy, Munich/Muenchen.
>Environment:
System: FreeBSD blak.js.berklix.net 8.1-RELEASE FreeBSD 8.1-RELEASE #2: Fri Aug 
20 15:11:19 CEST 2010 
j...@blak.js.berklix.net:/usr/src/sys/amd64/compile/BLAK.small amd64


        
>Description:
        usr.bin/tar/ ignores error codes from read() silently pads with nulls,
        corrupting copied data without warning !
        Log of how I discovered problem + diffs + copy of send-pr:
          http://berklix.com/~jhs/src/bsd/fixes/FreeBSD/src/gen/usr.bin/tar/
        (various files have .ignore extension to avoid my customise script).

        Comparison with gtar:
                gtar also delivers false nulls unfortunately,
                gtar warns users it is getting device errors.

        Comparison with cp:
                cp warns of errors,
                cp delivers no false nulls, truncates at read fail.

>How-To-Repeat:
- Get a normal data CD or DVD you have created )
  (not commercial recorded hollywood deliberately sector crippled media),
- Damage the media so it will have some read errors,
  mount /dev/acd0 /cdrom
  cd /tmp
  mkdir -p cp tar/cdrom
  cp -R /cdrom cp
  (cd /cdrom ; tar cf - . ) | ( cd tar/cdrom ; tar xf - )
  cd tar/cdrom
  # http://berklix.com/~jhs/src/bsd/jhs/bin/public/cmpd/
  find . -type f -exex cmpd -d {} ../../cp/cdrom \;
  # http://berklix.com/~jhs/src/bsd/jhs/bin/public/8f/
  foreach i ( `find . -type f | xargs 8f -n 2048 -b 0 -l` )
        echo $i
        xargs od -c $i | yail
        end

>Fix:
My patches fix code to avoid unwarned damaged data.  As it's Tim
K.'s recently written code, I presume he will want to fix it himself,
so my patch lines as well as fixing, are also appended with edit mark
// JHS for easy searching.

write.c & util.c were bad in 8.1-RELEASE 
        (& the critical bit in 
        ./-current/src/usr/bin/write.c
        ^write_file_data() 
        the final 
        bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN);
        is also bad)
Various return values silently ignored.  
All invocations of [f]read() & [f]write() & all invocations of
*_read_* & *_write_* etc should be checked whether return value is
properly used.

Later fixes if any will be in
http://berklix.com/~jhs/src/bsd/fixes/FreeBSD/src/gen/usr.bin/tar/
A copy appended.

*** 8.1-RELEASE-generic/src/usr.bin/tar/write.c Mon Jun 14 04:09:06 2010
--- 8.1-RELEASE+jhs-error-detect/src/usr.bin/tar/write.c        Sat Jan 29 
18:34:55 2011
***************
*** 217,222 ****
--- 217,224 ----
        if (ARCHIVE_OK != archive_write_open_file(a, bsdtar->filename))
                bsdtar_errc(bsdtar, 1, 0, archive_error_string(a));
        write_archive(a, bsdtar);
+               // JHS write_archive is declared void,
+               // JHS is there a static extern ret val to check ?
  }
  
  /*
***************
*** 301,312 ****
                archive_write_set_format(a, format);
        }
        lseek(bsdtar->fd, end_offset, SEEK_SET); /* XXX check return val XXX */
        if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options))
                bsdtar_errc(bsdtar, 1, 0, archive_error_string(a));
        if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd))
                bsdtar_errc(bsdtar, 1, 0, archive_error_string(a));
  
!       write_archive(a, bsdtar); /* XXX check return val XXX */
  
        close(bsdtar->fd);
        bsdtar->fd = -1;
--- 303,316 ----
                archive_write_set_format(a, format);
        }
        lseek(bsdtar->fd, end_offset, SEEK_SET); /* XXX check return val XXX */
+                                                               // JHS
        if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options))
                bsdtar_errc(bsdtar, 1, 0, archive_error_string(a));
        if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd))
                bsdtar_errc(bsdtar, 1, 0, archive_error_string(a));
  
!       write_archive(a, bsdtar); /* XXX check return val XXX */        
!               // JHS what return val ? write_archive is declared void
  
        close(bsdtar->fd);
        bsdtar->fd = -1;
***************
*** 390,395 ****
--- 394,401 ----
                bsdtar_errc(bsdtar, 1, 0, archive_error_string(a));
  
        write_archive(a, bsdtar);
+               // JHS write_archive is declared void,
+               // JHS is there a static extern ret val to check ?
  
        close(bsdtar->fd);
        bsdtar->fd = -1;
***************
*** 926,931 ****
--- 932,945 ----
        off_t   progress = 0;
  
        bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN);
+       if (bytes_read < 0) {                                   // JHS
+               perror(NULL) ;                                  // JHS
+               fprintf(stderr,"File: %s, Line %d, Ret %d\n",   // JHS
+                       __FILE__, __LINE__, (int)bytes_read );  // JHS
+               return(-1) ;                                    // JHS
+               // I've not checked to see if caller            // JHS
+               // appropriately detects & deals with -1        // JHS
+               }                                               // JHS
        while (bytes_read > 0) {
                siginfo_printinfo(bsdtar, progress);
  
***************
*** 945,951 ****
                }
                progress += bytes_written;
                bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN);
!       }
        return 0;
  }
  
--- 959,995 ----
                }
                progress += bytes_written;
                bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN);
!               if (bytes_read < 0)                             // JHS
!                       {                                       // JHS
!                       perror(NULL) ;                          // JHS
!                       fprintf(stderr,"Read error.\n");        // JHS
!                       // fprintf(stderr,"Read error on %s.\n",        // JHS
!                       // Please pass in name as parameter to print. // JHS
!                       //      "" );                           // JHS
!                       fprintf(stderr,                         // JHS
!                        "Output will contain false trailing nulls.\n"); // JHS
!                       // This prints when 
!                       fprintf(stderr,                         // JHS
!                               "File: %s, Line %d, Ret %d\n",  // JHS
!                        __FILE__, __LINE__, (int)bytes_read ); // JHS
!                       return(-1) ;                            // JHS
!                       // I've not read code to see if caller  // JHS
!                       // appropriately detects & deals with -1 // JHS
!                       // But Ive tested it, with              // JHS
!                       // tar cvf junk.tar aa bb,              // JHS
!                       // & if aa has dev errors,              // JHS
!                       // bb does not get archived.            // JHS
!                       }                                       // JHS
!       }
!       if (bytes_read < 0)                                     // JHS
!               {                                               // JHS
!               perror(NULL) ;                                  // JHS
!               fprintf(stderr,"File: %s, Line %d\n",           // JHS
!                       __FILE__, __LINE__ );                   // JHS
!               return(-1) ;                                    // JHS
!               // I've not read code to see if caller          // JHS
!               // appropriately detects & deals with -1        // JHS
!               }                                               // JHS
        return 0;
  }
  
*** 8.1-RELEASE-generic/src/usr.bin/tar/util.c  Mon Jun 14 04:09:06 2010
--- 8.1-RELEASE+jhs-error-detect/src/usr.bin/tar/util.c Sat Jan 29 16:36:45 2011
***************
*** 234,239 ****
--- 234,241 ----
        exit(eval);
  }
  
+ /* Get confirmation from user to do something, eg add/ copy           // JHS
+       Return: 1=yes, 0=no */                                          // JHS
  int
  yes(const char *fmt, ...)
  {
***************
*** 249,254 ****
--- 251,263 ----
        fflush(stderr);
  
        l = read(2, buff, sizeof(buff) - 1);
+       if (l < 0)                                                      // JHS
+               {                                                       // JHS
+               perror(NULL) ;                                          // JHS
+               fprintf(stderr,"Failed to read yes/no\n" );             // JHS
+               fprintf(stderr,"File: %s, Line %d, Ret %d\n",           // JHS
+                       __FILE__, __LINE__, (int)l );                   // JHS  
+               }                                                       // JHS
        if (l <= 0)
                return (0);
        buff[l] = 0;
***************
*** 307,312 ****
--- 316,333 ----
                /* Get some more data into the buffer. */
                bytes_wanted = buff + buff_length - buff_end;
                bytes_read = fread(buff_end, 1, bytes_wanted, f);
+               if (                                                    // JHS
+                       ( bytes_read < bytes_wanted )                   // JHS
+                       ||                                              // JHS
+                       (bytes_read = 0)                                // JHS
+                  )                                                    // JHS
+               if ((bytes_read = 0) || ( bytes_read < bytes_wanted ))  // JHS
+                       {                                               // JHS
+                       perror(NULL) ;                                  // JHS
+                       fprintf(stderr,                                 // JHS
+                               "File: %s, Line %d, Ret %d\n",          // JHS
+                               __FILE__, __LINE__, (int)bytes_read );  // JHS
+                       }                                               // JHS
                buff_end += bytes_read;
                /* Process all complete lines in the buffer. */
                while (line_end < buff_end) {
>Release-Note:
>Audit-Trail:
>Unformatted:
_______________________________________________
freebsd-bugs@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"

Reply via email to