bug-tar
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug-tar] Two problems with 'tar -W' with tapes


From: Kai Makisara
Subject: [Bug-tar] Two problems with 'tar -W' with tapes
Date: Sat, 5 Mar 2005 13:39:29 +0200 (EET)

The Linux kernels were recently changed to return error when lseek() was used
with tapes. This lead to reports about 'tar -c -W' failing at the beginning of
a tape. Examination of this problem revealed two problems in tar. A
proof-of-concept patch against tar-1.15.1 for one solution to these problems
is at the end of this message.

The problems:

1. Tar seems to assume that the tape driver automatically writes a file mark
   when the tape is spaced backwards after writing. This is not true in Linux 
and
   the man page of one other Unix explicitly says that a filemark is not written
   in this case. One other Unix does this based on the man page. Other man pages
   I looked at did not say anything about this.

   A simple solution to this is to explicitly write a filemark before spacing.

2. Tar relies on that lseek() on a tape device returns success even when it
   does not do anything. Some Unix man pages say that lseek() is ignored,
   others don't say anything.

   This problem is hit when trying to verify an archive at the beginning of a
   tape. The code in verify_volume() in compare.c has the following logic:

    operation.mt_op = MTBSF;
    operation.mt_count = 1;
    if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
      {
        if (errno != EIO
            || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
                status < 0))
          {
#endif
            if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
              {
                /* Lseek failed.  Try a different method.  */
                seek_warn (archive_name_array[0]);
                return;

   At the beginning of the tape the BSF fails with errno = EIO because there
   is no filemark at the beginning of the tape. If lseek() returns success,
   the code works by luck because, as side effect, the failing BSFs have moved
   the tape to BOT (Beginning Of Tape).

   The solution in the patch below is to check if we are at BOT if BSF
   fails. A better solution would be to do this check also when starting to
   write but it would need changes also at other places and may not be worth
   the trouble (this patch does not change anything outside the verify path).

   The at_bot() function prefers GMT_BOT() over checking file and block
   numbers. The reason is that HPUX has GMT_BOT() and the file and block
   numbers but the man page says that the numbers are always -1.

The patch at the end has been tested with both a tape and a file with Linux
2.6.11. The aim of the patch is to be portable anywhere but I don't have
direct access to any other Unix systems with accessible tapes.

Kai

--- tar-1.15.1/src/compare.c    2004-09-06 14:30:29.000000000 +0300
+++ tar-1.15.1-km/src/compare.c 2005-03-05 13:03:57.000000000 +0200
@@ -491,6 +491,27 @@
     }
 }
 
+int at_bot(void)
+{
+    int retval = 0;
+
+#ifdef MTIOCGET
+    struct mtget m;
+
+    if (!rmtioctl(archive, MTIOCGET, (char *) &m))
+    {
+#ifdef GMT_BOT
+       if (GMT_BOT(m.mt_gstat))
+           retval = 1;
+#else
+       if (m.mt_fileno == 0 && m.mt_blkno == 0)
+           retval = 1;
+#endif
+    }
+#endif
+    return retval;
+}
+
 void
 verify_volume (void)
 {
@@ -525,15 +546,28 @@
 #ifdef MTIOCTOP
   {
     struct mtop operation;
-    int status;
+    int status, bsfcount = 1;
+
+#ifdef MTWEOF
+    if (access_mode == ACCESS_WRITE)
+      {
+       operation.mt_op = MTWEOF;
+       operation.mt_count = 1;
+       rmtioctl (archive, MTIOCTOP, (char *) &operation);
+       bsfcount++;
+      }
+#endif
 
     operation.mt_op = MTBSF;
-    operation.mt_count = 1;
+    operation.mt_count = bsfcount;
     if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
       {
-       if (errno != EIO
+         int errnol = errno;
+
+         if (!at_bot() &&
+           (errnol != EIO
            || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
-               status < 0))
+               status < 0)))
          {
 #endif
            if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]