diff options
Diffstat (limited to 'src/system.c')
-rw-r--r-- | src/system.c | 81 |
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 |