From: Sergey Poznyakoff Date: Tue, 8 Sep 2009 07:27:57 +0000 (+0300) Subject: Automatic detection of seekable archives. X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=cef4d5e83830763a1acd5d603fb62a356df27299;p=chaz%2Ftar Automatic detection of seekable archives. * src/buffer.c (guess_seekable_archive): New function. (_open_archive): Call guess_seekable_archive for archives open for reading. (new_volume): Likewise. * src/common.h (seek_option): New global. * src/tar.c (options): New option --no-seek. (parse_opt): --seek and --no-seek set seek_option, not seekable_archive. (decode_options): Initialize seek_option to -1. * NEWS: Update. * doc/tar.texi: Update. --- diff --git a/NEWS b/NEWS index 5e796b2..bf02a24 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -GNU tar NEWS - User visible changes. 2009-08-08 +GNU tar NEWS - User visible changes. 2009-09-08 Please send GNU tar bug reports to @@ -10,6 +10,11 @@ When listing or extracting archives, the actual record size is reported only if the archive is read from a device (as opposed to regular files and pipes). +* Seekable archives + +When a read-only operation (e.g. --list or --extract) is requested +on a regular file, tar attemtps to speed up accesses by using lseek. + * New command line option `--warning' The `--warning' command line option allows to suppress or enable diff --git a/doc/tar.texi b/doc/tar.texi index 7154dfd..e609368 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -2950,6 +2950,14 @@ When extracting an archive, subtract the user's umask from files from the permissions specified in the archive. This is the default behavior for ordinary users. +@opsummary{no-seek} +@item --no-seek + +The archive media does not support seeks to arbitrary +locations. Usually @command{tar} determines automatically whether +the archive can be seeked or not. Use this option to disable this +mechanism. + @opsummary{no-unquote} @item --no-unquote Treat all input file or member names literally, do not interpret @@ -3180,7 +3188,9 @@ effect only for ordinary users. @xref{Attributes}. Assume that the archive media supports seeks to arbitrary locations. Usually @command{tar} determines automatically whether the archive can be seeked or not. This option is intended for use -in cases when such recognition fails. +in cases when such recognition fails. It takes effect only if the +archive is open for reading (e.g. with @option{--list} or +@option{--extract} options). @opsummary{show-defaults} @item --show-defaults diff --git a/src/buffer.c b/src/buffer.c index a01af37..dd97682 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -265,6 +265,37 @@ check_compressed_archive (bool *pshort) return ct_none; } +/* Guess if the archive is seekable. */ +static void +guess_seekable_archive () +{ + struct stat st; + + if (subcommand_option == DELETE_SUBCOMMAND) + { + /* The current code in delete.c is based on the assumption that + skip_member() reads all data from the archive. So, we should + make sure it won't use seeks. On the other hand, the same code + depends on the ability to backspace a record in the archive, + so setting seekable_archive to false is technically incorrect. + However, it is tested only in skip_member(), so it's not a + problem. */ + seekable_archive = false; + } + + if (seek_option != -1) + { + seekable_archive = !!seek_option; + return; + } + + if (!multi_volume_option && !use_compress_program_option + && fstat (archive, &st) == 0) + seekable_archive = S_ISREG (st.st_mode); + else + seekable_archive = false; +} + /* Open an archive named archive_name_array[0]. Detect if it is a compressed archive of known type and use corresponding decompression program if so */ @@ -295,7 +326,7 @@ open_compressed_archive () ERROR ((0, 0, _("This does not look like a tar archive"))); set_comression_program_by_suffix (archive_name_array[0], NULL); if (!use_compress_program_option) - return archive; + return archive; break; default: @@ -306,7 +337,7 @@ open_compressed_archive () /* FD is not needed any more */ rmtclose (archive); - + hit_eof = false; /* It might have been set by find_next_block in check_compressed_archive */ @@ -565,6 +596,7 @@ _open_archive (enum access_mode wanted_access) { case ACCESS_READ: archive = open_compressed_archive (); + guess_seekable_archive (); break; case ACCESS_WRITE: @@ -1098,6 +1130,7 @@ new_volume (enum access_mode mode) case ACCESS_READ: archive = rmtopen (*archive_name_cursor, O_RDONLY, MODE_RW, rsh_command_option); + guess_seekable_archive (); break; case ACCESS_WRITE: diff --git a/src/common.h b/src/common.h index f2cdf2b..196c118 100644 --- a/src/common.h +++ b/src/common.h @@ -354,6 +354,7 @@ struct name GLOBAL dev_t ar_dev; GLOBAL ino_t ar_ino; +GLOBAL int seek_option; GLOBAL bool seekable_archive; GLOBAL dev_t root_device; diff --git a/src/tar.c b/src/tar.c index 3aa5287..583edc9 100644 --- a/src/tar.c +++ b/src/tar.c @@ -291,6 +291,7 @@ enum NO_RECURSION_OPTION, NO_SAME_OWNER_OPTION, NO_SAME_PERMISSIONS_OPTION, + NO_SEEK_OPTION, NO_UNQUOTE_OPTION, NO_WILDCARDS_MATCH_SLASH_OPTION, NO_WILDCARDS_OPTION, @@ -366,7 +367,12 @@ The version control may be set with --backup or VERSION_CONTROL, values are:\n\n [q alias for --occurrence=1 =/= this would better be used for quiet?] y per-file gzip compression - Y per-block gzip compression */ + Y per-block gzip compression. + + Additionally, the 'n' letter is assigned for option --seek, which + is probably not needed and should be marked as deprecated, so that + -n may become available in the future. +*/ static struct argp_option options[] = { #define GRID 10 @@ -420,6 +426,8 @@ static struct argp_option options[] = { " NUMBER defaults to 1"), GRID+1 }, {"seek", 'n', NULL, 0, N_("archive is seekable"), GRID+1 }, + {"no-seek", NO_SEEK_OPTION, NULL, 0, + N_("archive is not seekable"), GRID+1 }, {"no-check-device", NO_CHECK_DEVICE_OPTION, NULL, 0, N_("do not check device numbers when creating incremental archives"), GRID+1 }, @@ -1457,9 +1465,13 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'n': - seekable_archive = true; + seek_option = 1; break; + case NO_SEEK_OPTION: + seek_option = 0; + break; + case 'N': after_date_option = true; /* Fall through. */ @@ -2147,6 +2159,8 @@ decode_options (int argc, char **argv) check_device_option = true; incremental_level = -1; + + seek_option = -1; /* Convert old-style tar call by exploding option element and rearranging options accordingly. */ @@ -2279,18 +2293,6 @@ decode_options (int argc, char **argv) _("--occurrence cannot be used in the requested operation mode"))); } - if (seekable_archive && subcommand_option == DELETE_SUBCOMMAND) - { - /* The current code in delete.c is based on the assumption that - skip_member() reads all data from the archive. So, we should - make sure it won't use seeks. On the other hand, the same code - depends on the ability to backspace a record in the archive, - so setting seekable_archive to false is technically incorrect. - However, it is tested only in skip_member(), so it's not a - problem. */ - seekable_archive = false; - } - if (archive_names == 0) { /* If no archive file name given, try TAPE from the environment, or