summaryrefslogtreecommitdiff
path: root/src/system.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/system.c')
-rw-r--r--src/system.c81
1 files changed, 55 insertions, 26 deletions
diff --git a/src/system.c b/src/system.c
index 1f560783..9f8bbe4c 100644
--- a/src/system.c
+++ b/src/system.c
@@ -16,6 +16,7 @@
with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <system.h>
+#include <system-ioctl.h>
#include "common.h"
#include <priv-set.h>
@@ -37,6 +38,35 @@ xexec (const char *cmd)
exec_fatal (cmd);
}
+/* True if the archive is seekable via ioctl and MTIOCTOP,
+ or if it is not known whether it is seekable.
+ False if it is known to be not seekable. */
+static bool mtioseekable_archive;
+
+bool
+mtioseek (bool count_files, off_t count)
+{
+ if (mtioseekable_archive)
+ {
+#ifdef MTIOCTOP
+ struct mtop operation;
+ operation.mt_op = (count_files
+ ? (count < 0 ? MTBSF : MTFSF)
+ : (count < 0 ? MTBSR : MTFSR));
+ if (! (count < 0
+ ? INT_SUBTRACT_WRAPV (0, count, &operation.mt_count)
+ : INT_ADD_WRAPV (count, 0, &operation.mt_count))
+ && (0 <= rmtioctl (archive, MTIOCTOP, &operation)
+ || (errno == EIO
+ && 0 <= rmtioctl (archive, MTIOCTOP, &operation))))
+ return true;
+#endif
+
+ mtioseekable_archive = false;
+ }
+ return false;
+}
+
#if MSDOS
bool
@@ -52,11 +82,6 @@ sys_file_is_archive (struct tar_stat_info *p)
}
void
-sys_save_archive_dev_ino (void)
-{
-}
-
-void
sys_detect_dev_null_output (void)
{
static char const dev_null[] = "nul";
@@ -128,31 +153,34 @@ sys_child_open_for_uncompress (void)
extern union block *record_start; /* FIXME */
-static struct stat archive_stat; /* stat block for archive file */
-
bool
sys_get_archive_stat (void)
{
- return fstat (archive, &archive_stat) == 0;
+ bool remote = _isrmt (archive);
+ mtioseekable_archive = true;
+ if (!remote && 0 <= archive && fstat (archive, &archive_stat) == 0)
+ {
+ if (!S_ISCHR (archive_stat.st_mode))
+ mtioseekable_archive = false;
+ return true;
+ }
+ else
+ {
+ /* FIXME: This memset should not be needed. It is present only
+ because other parts of tar may incorrectly access
+ archive_stat even if it's not the archive status. */
+ memset (&archive_stat, 0, sizeof archive_stat);
+
+ return remote;
+ }
}
bool
sys_file_is_archive (struct tar_stat_info *p)
{
- return (ar_dev && p->stat.st_dev == ar_dev && p->stat.st_ino == ar_ino);
-}
-
-/* Save archive file inode and device numbers */
-void
-sys_save_archive_dev_ino (void)
-{
- if (!_isrmt (archive) && S_ISREG (archive_stat.st_mode))
- {
- ar_dev = archive_stat.st_dev;
- ar_ino = archive_stat.st_ino;
- }
- else
- ar_dev = 0;
+ return (!dev_null_output && !_isrmt (archive)
+ && p->stat.st_dev == archive_stat.st_dev
+ && p->stat.st_ino == archive_stat.st_ino);
}
/* Detect if outputting to "/dev/null". */
@@ -160,14 +188,15 @@ void
sys_detect_dev_null_output (void)
{
static char const dev_null[] = "/dev/null";
- struct stat dev_null_stat;
+ static struct stat dev_null_stat;
dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
|| (! _isrmt (archive)
&& S_ISCHR (archive_stat.st_mode)
- && stat (dev_null, &dev_null_stat) == 0
- && archive_stat.st_dev == dev_null_stat.st_dev
- && archive_stat.st_ino == dev_null_stat.st_ino));
+ && (dev_null_stat.st_ino != 0
+ || stat (dev_null, &dev_null_stat) == 0)
+ && archive_stat.st_ino == dev_null_stat.st_ino
+ && archive_stat.st_dev == dev_null_stat.st_dev));
}
void