From 7cfb61a9b9d4d229afc86b347be4b4f696159b61 Mon Sep 17 00:00:00 2001 From: kientzle Date: Sun, 27 Jun 2004 18:38:13 +0000 Subject: Clean up some consistent confusion between "dev" and "rdev." Mostly, these were being used correctly even though a lot of variables and function names were mis-named. In the process, I found and fixed a couple of latent bugs and added a guard against adding an archive to itself. --- lib/libarchive/archive_entry.c | 72 +++++++++++++++--------- lib/libarchive/archive_entry.h | 11 ++-- lib/libarchive/archive_private.h | 7 ++- lib/libarchive/archive_read_extract.c | 8 +-- lib/libarchive/archive_read_support_format_tar.c | 24 ++++---- lib/libarchive/archive_write.c | 7 +++ lib/libarchive/archive_write_open_fd.c | 24 +++++--- lib/libarchive/archive_write_open_file.c | 32 ++++++++--- lib/libarchive/archive_write_open_filename.c | 32 ++++++++--- lib/libarchive/archive_write_set_format_pax.c | 32 ++++++----- lib/libarchive/archive_write_set_format_shar.c | 8 +-- lib/libarchive/archive_write_set_format_ustar.c | 20 +++---- 12 files changed, 176 insertions(+), 101 deletions(-) (limited to 'lib/libarchive') diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index dfea3d3..0007e7a 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -339,16 +339,9 @@ archive_entry_new(void) */ dev_t -archive_entry_devmajor(struct archive_entry *entry) +archive_entry_dev(struct archive_entry *entry) { - return (major(entry->ae_stat.st_rdev)); -} - - -dev_t -archive_entry_devminor(struct archive_entry *entry) -{ - return (minor(entry->ae_stat.st_rdev)); + return (entry->ae_stat.st_dev); } void @@ -403,13 +396,18 @@ archive_entry_hardlink(struct archive_entry *entry) return (aes_get_mbs(&entry->ae_hardlink)); } +ino_t +archive_entry_ino(struct archive_entry *entry) +{ + return (entry->ae_stat.st_ino); +} + mode_t archive_entry_mode(struct archive_entry *entry) { return (entry->ae_stat.st_mode); } - time_t archive_entry_mtime(struct archive_entry *entry) { @@ -434,6 +432,24 @@ archive_entry_pathname_w(struct archive_entry *entry) return (aes_get_wcs(&entry->ae_pathname)); } +dev_t +archive_entry_rdev(struct archive_entry *entry) +{ + return (entry->ae_stat.st_rdev); +} + +dev_t +archive_entry_rdevmajor(struct archive_entry *entry) +{ + return (major(entry->ae_stat.st_rdev)); +} + +dev_t +archive_entry_rdevminor(struct archive_entry *entry) +{ + return (minor(entry->ae_stat.st_rdev)); +} + int64_t archive_entry_size(struct archive_entry *entry) { @@ -473,24 +489,6 @@ archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st) } void -archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) -{ - dev_t d; - - d = entry->ae_stat.st_rdev; - entry->ae_stat.st_rdev = makedev(m, minor(d)); -} - -void -archive_entry_set_devminor(struct archive_entry *entry, dev_t m) -{ - dev_t d; - - d = entry->ae_stat.st_rdev; - entry->ae_stat.st_rdev = makedev( major(d), m); -} - -void archive_entry_set_fflags(struct archive_entry *entry, unsigned long set, unsigned long clear) { @@ -573,6 +571,24 @@ archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) } void +archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) +{ + dev_t d; + + d = entry->ae_stat.st_rdev; + entry->ae_stat.st_rdev = makedev(m, minor(d)); +} + +void +archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) +{ + dev_t d; + + d = entry->ae_stat.st_rdev; + entry->ae_stat.st_rdev = makedev( major(d), m); +} + +void archive_entry_set_size(struct archive_entry *entry, int64_t s) { entry->ae_stat.st_size = s; diff --git a/lib/libarchive/archive_entry.h b/lib/libarchive/archive_entry.h index 79054fb..b725a3b 100644 --- a/lib/libarchive/archive_entry.h +++ b/lib/libarchive/archive_entry.h @@ -66,18 +66,21 @@ struct archive_entry *archive_entry_new(void); * Retrieve fields from an archive_entry. */ -dev_t archive_entry_devmajor(struct archive_entry *); -dev_t archive_entry_devminor(struct archive_entry *); +dev_t archive_entry_dev(struct archive_entry *); const char *archive_entry_fflags_text(struct archive_entry *); void archive_entry_fflags(struct archive_entry *, unsigned long *set, unsigned long *clear); const char *archive_entry_gname(struct archive_entry *); const char *archive_entry_hardlink(struct archive_entry *); +ino_t archive_entry_ino(struct archive_entry *); mode_t archive_entry_mode(struct archive_entry *); time_t archive_entry_mtime(struct archive_entry *); long archive_entry_mtime_nsec(struct archive_entry *); const char *archive_entry_pathname(struct archive_entry *); const wchar_t *archive_entry_pathname_w(struct archive_entry *); +dev_t archive_entry_rdev(struct archive_entry *); +dev_t archive_entry_rdevmajor(struct archive_entry *); +dev_t archive_entry_rdevminor(struct archive_entry *); int64_t archive_entry_size(struct archive_entry *); const struct stat *archive_entry_stat(struct archive_entry *); const char *archive_entry_symlink(struct archive_entry *); @@ -91,8 +94,6 @@ const char *archive_entry_uname(struct archive_entry *); */ void archive_entry_copy_stat(struct archive_entry *, const struct stat *); -void archive_entry_set_devmajor(struct archive_entry *, dev_t); -void archive_entry_set_devminor(struct archive_entry *, dev_t); void archive_entry_set_fflags(struct archive_entry *, unsigned long set, unsigned long clear); /* Returns pointer to start of first invalid token, or NULL if none. */ @@ -109,6 +110,8 @@ void archive_entry_set_link(struct archive_entry *, const char *); void archive_entry_set_mode(struct archive_entry *, mode_t); void archive_entry_set_pathname(struct archive_entry *, const char *); void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); +void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); +void archive_entry_set_rdevminor(struct archive_entry *, dev_t); void archive_entry_set_size(struct archive_entry *, int64_t); void archive_entry_set_symlink(struct archive_entry *, const char *); void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); diff --git a/lib/libarchive/archive_private.h b/lib/libarchive/archive_private.h index 713584f..730974c 100644 --- a/lib/libarchive/archive_private.h +++ b/lib/libarchive/archive_private.h @@ -48,6 +48,11 @@ struct archive { unsigned state; struct archive_entry *entry; + uid_t user_uid; /* UID of current user. */ + + /* Dev/ino of the archive being read/written. */ + dev_t skip_file_dev; + ino_t skip_file_ino; /* Utility: Pointer to a block of nulls. */ const char *nulls; @@ -62,8 +67,6 @@ struct archive { off_t read_data_output_offset; size_t read_data_remaining; - uid_t user_uid; /* UID of current user. */ - /* Callbacks to open/read/write/close archive stream. */ archive_open_callback *client_opener; archive_read_callback *client_reader; diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index c473980..1257a8e 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -625,13 +625,13 @@ archive_read_extract_device(struct archive *a, struct archive_entry *entry, unlink(archive_entry_pathname(entry)); r = mknod(archive_entry_pathname(entry), mode, - archive_entry_stat(entry)->st_rdev); + archive_entry_rdev(entry)); /* Might be a non-existent parent dir; try fixing that. */ if (r != 0 && errno == ENOENT) { mkdirpath(a, archive_entry_pathname(entry)); r = mknod(archive_entry_pathname(entry), mode, - archive_entry_stat(entry)->st_rdev); + archive_entry_rdev(entry)); } if (r != 0) { @@ -654,7 +654,7 @@ archive_read_extract_char_device(struct archive *a, { mode_t mode; - mode = (archive_entry_stat(entry)->st_mode & ~S_IFMT) | S_IFCHR; + mode = (archive_entry_mode(entry) & ~S_IFMT) | S_IFCHR; return (archive_read_extract_device(a, entry, flags, mode)); } @@ -664,7 +664,7 @@ archive_read_extract_block_device(struct archive *a, { mode_t mode; - mode = (archive_entry_stat(entry)->st_mode & ~S_IFMT) | S_IFBLK; + mode = (archive_entry_mode(entry) & ~S_IFMT) | S_IFBLK; return (archive_read_extract_device(a, entry, flags, mode)); } diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index 205f13d..ce71675 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -55,8 +55,8 @@ struct archive_entry_header_ustar { char version[2]; /* For POSIX: "00" */ char uname[32]; char gname[32]; - char devmajor[8]; - char devminor[8]; + char rdevmajor[8]; + char rdevminor[8]; char prefix[155]; }; @@ -81,8 +81,8 @@ struct archive_entry_header_gnutar { char magic[8]; /* "ustar \0" (note blank/blank/null at end) */ char uname[32]; char gname[32]; - char devmajor[8]; - char devminor[8]; + char rdevmajor[8]; + char rdevminor[8]; char atime[12]; char ctime[12]; char offset[12]; @@ -313,7 +313,7 @@ archive_read_format_tar_bid(struct archive *a) /* Not a valid mode; bail out here. */ return (0); } - /* TODO: Sanity test uid/gid/size/mtime/devmajor/devminor fields. */ + /* TODO: Sanity test uid/gid/size/mtime/rdevmajor/rdevminor fields. */ return (bid); } @@ -932,8 +932,8 @@ header_ustar(struct archive *a, struct tar *tar, struct archive_entry *entry, /* Parse out device numbers only for char and block specials. */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { st->st_rdev = makedev( - tar_atol(header->devmajor, sizeof(header->devmajor)), - tar_atol(header->devminor, sizeof(header->devminor))); + tar_atol(header->rdevmajor, sizeof(header->rdevmajor)), + tar_atol(header->rdevminor, sizeof(header->rdevminor))); } tar->entry_bytes_remaining = st->st_size; @@ -1070,9 +1070,9 @@ pax_attribute(struct archive_entry *entry, struct stat *st, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); else if (wcscmp(key, L"SCHILY.devmajor")==0) st->st_rdev = makedev(tar_atol10(value, wcslen(value)), - minor(st->st_dev)); + minor(st->st_rdev)); else if (wcscmp(key, L"SCHILY.devminor")==0) - st->st_rdev = makedev(major(st->st_dev), + st->st_rdev = makedev(major(st->st_rdev), tar_atol10(value, wcslen(value))); else if (wcscmp(key, L"SCHILY.fflags")==0) archive_entry_copy_fflags_text_w(entry, value); @@ -1234,8 +1234,8 @@ header_gnutar(struct archive *a, struct tar *tar, struct archive_entry *entry, /* Parse out device numbers only for char and block specials */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') st->st_rdev = makedev ( - tar_atol(header->devmajor, sizeof(header->devmajor)), - tar_atol(header->devminor, sizeof(header->devminor))); + tar_atol(header->rdevmajor, sizeof(header->rdevmajor)), + tar_atol(header->rdevminor, sizeof(header->rdevminor))); else st->st_rdev = 0; @@ -1333,7 +1333,7 @@ gnu_parse_sparse_data(struct archive *a, struct tar *tar, * all of the standard numeric fields. This is a significant limitation * in practice: * = file size is limited to 8GB - * = devmajor and devminor are limited to 21 bits + * = rdevmajor and rdevminor are limited to 21 bits * = uid/gid are limited to 21 bits * * There are two workarounds for this: diff --git a/lib/libarchive/archive_write.c b/lib/libarchive/archive_write.c index 6f872f1..f910f14 100644 --- a/lib/libarchive/archive_write.c +++ b/lib/libarchive/archive_write.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include #include "archive.h" +#include "archive_entry.h" #include "archive_private.h" extern char **environ; @@ -186,6 +187,12 @@ archive_write_header(struct archive *a, struct archive_entry *entry) if (a->state & ARCHIVE_STATE_DATA) ((a->format_finish_entry)(a)); + if (archive_entry_dev(entry) == a->skip_file_dev && + archive_entry_ino(entry) == a->skip_file_ino) { + archive_set_error(a, 0, "Can't add archive to itself."); + return (ARCHIVE_WARN); + } + /* Format and write header. */ ret = ((a->format_write_header)(a, entry)); diff --git a/lib/libarchive/archive_write_open_fd.c b/lib/libarchive/archive_write_open_fd.c index 21c7074..ae8c2e2 100644 --- a/lib/libarchive/archive_write_open_fd.c +++ b/lib/libarchive/archive_write_open_fd.c @@ -64,8 +64,9 @@ static int file_open(struct archive *a, void *client_data) { struct write_fd_data *mine; - struct stat st; + struct stat st, *pst; + pst = NULL; mine = client_data; /* @@ -75,12 +76,14 @@ file_open(struct archive *a, void *client_data) */ if (mine->fd >= 0 && a->bytes_in_last_block < 0) { /* Last block will be fully padded. */ - fstat(mine->fd, &st); - if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || - S_ISFIFO(st.st_mode)) - archive_write_set_bytes_in_last_block(a, 0); - else - archive_write_set_bytes_in_last_block(a, 1); + if (fstat(mine->fd, &st) == 0) { + pst = &st; + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || + S_ISFIFO(st.st_mode)) + archive_write_set_bytes_in_last_block(a, 0); + else + archive_write_set_bytes_in_last_block(a, 1); + } } if (mine->fd == 1) { @@ -94,6 +97,13 @@ file_open(struct archive *a, void *client_data) return (ARCHIVE_FATAL); } + if (pst == NULL && fstat(mine->fd, &st) == 0) + pst = &st; + if (pst == NULL) { + archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd); + return (ARCHIVE_FATAL); + } + return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_write_open_file.c b/lib/libarchive/archive_write_open_file.c index e562395..cfce0a5 100644 --- a/lib/libarchive/archive_write_open_file.c +++ b/lib/libarchive/archive_write_open_file.c @@ -76,8 +76,9 @@ file_open(struct archive *a, void *client_data) { int flags; struct write_file_data *mine; - struct stat st; + struct stat st, *pst; + pst = NULL; mine = client_data; flags = O_WRONLY | O_CREAT | O_TRUNC; @@ -91,13 +92,17 @@ file_open(struct archive *a, void *client_data) * otherwise leave it unpadded. */ if (mine->fd >= 0 && a->bytes_in_last_block < 0) { - /* Last block will be fully padded. */ - fstat(mine->fd, &st); - if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || - S_ISFIFO(st.st_mode)) - archive_write_set_bytes_in_last_block(a, 0); - else - archive_write_set_bytes_in_last_block(a, 1); + if (fstat(mine->fd, &st) == 0) { + pst = &st; + if (S_ISCHR(st.st_mode) || + S_ISBLK(st.st_mode) || + S_ISFIFO(st.st_mode)) + /* Pad last block. */ + archive_write_set_bytes_in_last_block(a, 0); + else + /* Don't pad last block. */ + archive_write_set_bytes_in_last_block(a, 1); + } } } else { mine->fd = 1; @@ -112,6 +117,17 @@ file_open(struct archive *a, void *client_data) return (ARCHIVE_FATAL); } + if (pst == NULL && fstat(mine->fd, &st) == 0) + pst = &st; + if (pst == NULL) { + archive_set_error(a, errno, "Couldn't stat '%s'", + mine->filename); + return (ARCHIVE_FATAL); + } + + a->skip_file_dev = pst->st_dev; + a->skip_file_ino = pst->st_ino; + return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_write_open_filename.c b/lib/libarchive/archive_write_open_filename.c index e562395..cfce0a5 100644 --- a/lib/libarchive/archive_write_open_filename.c +++ b/lib/libarchive/archive_write_open_filename.c @@ -76,8 +76,9 @@ file_open(struct archive *a, void *client_data) { int flags; struct write_file_data *mine; - struct stat st; + struct stat st, *pst; + pst = NULL; mine = client_data; flags = O_WRONLY | O_CREAT | O_TRUNC; @@ -91,13 +92,17 @@ file_open(struct archive *a, void *client_data) * otherwise leave it unpadded. */ if (mine->fd >= 0 && a->bytes_in_last_block < 0) { - /* Last block will be fully padded. */ - fstat(mine->fd, &st); - if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || - S_ISFIFO(st.st_mode)) - archive_write_set_bytes_in_last_block(a, 0); - else - archive_write_set_bytes_in_last_block(a, 1); + if (fstat(mine->fd, &st) == 0) { + pst = &st; + if (S_ISCHR(st.st_mode) || + S_ISBLK(st.st_mode) || + S_ISFIFO(st.st_mode)) + /* Pad last block. */ + archive_write_set_bytes_in_last_block(a, 0); + else + /* Don't pad last block. */ + archive_write_set_bytes_in_last_block(a, 1); + } } } else { mine->fd = 1; @@ -112,6 +117,17 @@ file_open(struct archive *a, void *client_data) return (ARCHIVE_FATAL); } + if (pst == NULL && fstat(mine->fd, &st) == 0) + pst = &st; + if (pst == NULL) { + archive_set_error(a, errno, "Couldn't stat '%s'", + mine->filename); + return (ARCHIVE_FATAL); + } + + a->skip_file_dev = pst->st_dev; + a->skip_file_ino = pst->st_ino; + return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c index 950cd4d..937113a 100644 --- a/lib/libarchive/archive_write_set_format_pax.c +++ b/lib/libarchive/archive_write_set_format_pax.c @@ -446,26 +446,28 @@ archive_write_pax_header(struct archive *a, /* * POSIX/SUSv3 doesn't provide a standard key for large device - * numbers. I use the same keys here that Joerg Schilling used for - * 'star.' No doubt, other implementations use other keys. Note that - * there's no reason we can't write the same information into a number - * of different keys. + * numbers. I use the same keys here that Joerg Schilling + * used for 'star.' (Which, somewhat confusingly, are called + * "devXXX" even though they code "rdev" values.) No doubt, + * other implementations use other keys. Note that there's no + * reason we can't write the same information into a number of + * different keys. * * Of course, this is only needed for block or char device entries. */ if (S_ISBLK(st_main->st_mode) || S_ISCHR(st_main->st_mode)) { /* - * If devmajor is too large, add 'SCHILY.devmajor' to + * If rdevmajor is too large, add 'SCHILY.devmajor' to * extended attributes. */ - dev_t devmajor, devminor; - devmajor = major(st_main->st_rdev); - devminor = minor(st_main->st_rdev); - if (devmajor >= (1 << 18)) { + dev_t rdevmajor, rdevminor; + rdevmajor = major(st_main->st_rdev); + rdevminor = minor(st_main->st_rdev); + if (rdevmajor >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor", - devmajor); - archive_entry_set_devmajor(entry_main, (1 << 18) - 1); + rdevmajor); + archive_entry_set_rdevmajor(entry_main, (1 << 18) - 1); need_extension = 1; } @@ -473,10 +475,10 @@ archive_write_pax_header(struct archive *a, * If devminor is too large, add 'SCHILY.devminor' to * extended attributes. */ - if (devminor >= (1 << 18)) { + if (rdevminor >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor", - devminor); - archive_entry_set_devminor(entry_main, (1 << 18) - 1); + rdevminor); + archive_entry_set_rdevminor(entry_main, (1 << 18) - 1); need_extension = 1; } } @@ -558,6 +560,8 @@ archive_write_pax_header(struct archive *a, "SCHILY.acl.default", wp); /* Include star-compatible metadata info. */ + /* Note: "SCHILY.dev{major,minor}" are NOT the + * major/minor portions of "SCHILY.dev". */ add_pax_attr_int(&(pax->pax_header), "SCHILY.dev", st_main->st_dev); add_pax_attr_int(&(pax->pax_header), "SCHILY.ino", diff --git a/lib/libarchive/archive_write_set_format_shar.c b/lib/libarchive/archive_write_set_format_shar.c index f66ab9e..2e3f348 100644 --- a/lib/libarchive/archive_write_set_format_shar.c +++ b/lib/libarchive/archive_write_set_format_shar.c @@ -275,13 +275,13 @@ archive_write_shar_header(struct archive *a, struct archive_entry *entry) break; case S_IFCHR: shar_printf(a, "mknod %s c %d %d\n", name, - archive_entry_devmajor(entry), - archive_entry_devminor(entry)); + archive_entry_rdevmajor(entry), + archive_entry_rdevminor(entry)); break; case S_IFBLK: shar_printf(a, "mknod %s b %d %d\n", name, - archive_entry_devmajor(entry), - archive_entry_devminor(entry)); + archive_entry_rdevmajor(entry), + archive_entry_rdevminor(entry)); break; default: return (ARCHIVE_WARN); diff --git a/lib/libarchive/archive_write_set_format_ustar.c b/lib/libarchive/archive_write_set_format_ustar.c index a7bd466..68892eb 100644 --- a/lib/libarchive/archive_write_set_format_ustar.c +++ b/lib/libarchive/archive_write_set_format_ustar.c @@ -65,10 +65,10 @@ struct archive_entry_header_ustar { char version[2]; /* For POSIX: "00" */ char uname[32]; char gname[32]; - char devmajor[6]; - char devmajor_padding[2]; - char devminor[6]; - char devminor_padding[2]; + char rdevmajor[6]; + char rdevmajor_padding[2]; + char rdevminor[6]; + char rdevminor_padding[2]; char prefix[155]; char padding[12]; }; @@ -90,8 +90,8 @@ static const struct archive_entry_header_ustar template_header = { { '0', '0' }, /* version */ { }, /* uname */ { }, /* gname */ - { "000000" }, { ' ', '\0' }, /* devmajor, space-null termination */ - { "000000" }, { ' ', '\0' }, /* devminor, space-null termination */ + { "000000" }, { ' ', '\0' }, /* rdevmajor, space-null termination */ + { "000000" }, { ' ', '\0' }, /* rdevminor, space-null termination */ { }, /* prefix */ { } /* padding */ }; @@ -289,15 +289,15 @@ __archive_write_format_header_ustar(struct archive *a, char buff[512], } if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { - if (format_octal(major(st->st_rdev), h->devmajor, - sizeof(h->devmajor))) { + if (format_octal(major(st->st_rdev), h->rdevmajor, + sizeof(h->rdevmajor))) { archive_set_error(a, ERANGE, "Major device number too large"); ret = ARCHIVE_WARN; } - if (format_octal(minor(st->st_rdev), h->devminor, - sizeof(h->devminor))) { + if (format_octal(minor(st->st_rdev), h->rdevminor, + sizeof(h->rdevminor))) { archive_set_error(a, ERANGE, "Minor device number too large"); ret = ARCHIVE_WARN; -- cgit v1.1