aboutsummaryrefslogtreecommitdiff
path: root/src/copyin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/copyin.c')
-rw-r--r--src/copyin.c1605
1 files changed, 1605 insertions, 0 deletions
diff --git a/src/copyin.c b/src/copyin.c
new file mode 100644
index 0000000..17e6744
--- /dev/null
+++ b/src/copyin.c
@@ -0,0 +1,1605 @@
+/* copyin.c - extract or list a cpio archive
+ Copyright (C) 1990,1991,1992,2001,2002,2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "filetypes.h"
+#include "system.h"
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "extern.h"
+#include "defer.h"
+#include "rmt.h"
+#ifndef FNM_PATHNAME
+#include <fnmatch.h>
+#endif
+
+#ifndef HAVE_LCHOWN
+#define lchown chown
+#endif
+
+static void read_pattern_file ();
+static void tape_skip_padding ();
+static void defer_copyin ();
+static void create_defered_links ();
+static void create_final_defers ();
+static int create_defered_links_to_skipped ();
+static int query_rename();
+static void list_file();
+static void copyin_file();
+static int try_existing_file();
+static void copyin_regular_file();
+static void copyin_directory();
+static void copyin_device();
+static void copyin_link();
+
+/* Return 16-bit integer I with the bytes swapped. */
+#define swab_short(i) ((((i) << 8) & 0xff00) | (((i) >> 8) & 0x00ff))
+
+/* Read the header, including the name of the file, from file
+ descriptor IN_DES into FILE_HDR. */
+
+void
+read_in_header (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ long bytes_skipped = 0; /* Bytes of junk found before magic number. */
+
+ /* Search for a valid magic number. */
+
+ if (archive_format == arf_unknown)
+ {
+ char tmpbuf[512];
+ int check_tar;
+ int peeked_bytes;
+
+ while (archive_format == arf_unknown)
+ {
+ peeked_bytes = tape_buffered_peek (tmpbuf, in_des, 512);
+ if (peeked_bytes < 6)
+ error (1, 0, _("premature end of archive"));
+
+ if (!strncmp (tmpbuf, "070701", 6))
+ archive_format = arf_newascii;
+ else if (!strncmp (tmpbuf, "070707", 6))
+ archive_format = arf_oldascii;
+ else if (!strncmp (tmpbuf, "070702", 6))
+ {
+ archive_format = arf_crcascii;
+ crc_i_flag = TRUE;
+ }
+ else if ((*((unsigned short *) tmpbuf) == 070707) ||
+ (*((unsigned short *) tmpbuf) == swab_short ((unsigned short) 070707)))
+ archive_format = arf_binary;
+ else if (peeked_bytes >= 512
+ && (check_tar = is_tar_header (tmpbuf)))
+ {
+ if (check_tar == 2)
+ archive_format = arf_ustar;
+ else
+ archive_format = arf_tar;
+ }
+ else
+ {
+ tape_buffered_read ((char *) tmpbuf, in_des, 1L);
+ ++bytes_skipped;
+ }
+ }
+ }
+
+ if (archive_format == arf_tar || archive_format == arf_ustar)
+ {
+ if (append_flag)
+ last_header_start = input_bytes - io_block_size +
+ (in_buff - input_buffer);
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_tar_header (file_hdr, in_des);
+ return;
+ }
+
+ file_hdr->c_tar_linkname = NULL;
+
+ tape_buffered_read ((char *) file_hdr, in_des, 6L);
+ while (1)
+ {
+ if (append_flag)
+ last_header_start = input_bytes - io_block_size
+ + (in_buff - input_buffer) - 6;
+ if (archive_format == arf_newascii
+ && !strncmp ((char *) file_hdr, "070701", 6))
+ {
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_new_ascii (file_hdr, in_des);
+ break;
+ }
+ if (archive_format == arf_crcascii
+ && !strncmp ((char *) file_hdr, "070702", 6))
+ {
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_new_ascii (file_hdr, in_des);
+ break;
+ }
+ if ( (archive_format == arf_oldascii || archive_format == arf_hpoldascii)
+ && !strncmp ((char *) file_hdr, "070707", 6))
+ {
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_old_ascii (file_hdr, in_des);
+ break;
+ }
+ if ( (archive_format == arf_binary || archive_format == arf_hpbinary)
+ && (file_hdr->c_magic == 070707
+ || file_hdr->c_magic == swab_short ((unsigned short) 070707)))
+ {
+ /* Having to skip 1 byte because of word alignment is normal. */
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_binary (file_hdr, in_des);
+ break;
+ }
+ bytes_skipped++;
+ bcopy ((char *) file_hdr + 1, (char *) file_hdr, 5);
+ tape_buffered_read ((char *) file_hdr + 5, in_des, 1L);
+ }
+}
+
+/* Fill in FILE_HDR by reading an old-format ASCII format cpio header from
+ file descriptor IN_DES, except for the magic number, which is
+ already filled in. */
+
+void
+read_in_old_ascii (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ char ascii_header[78];
+ unsigned long dev;
+ unsigned long rdev;
+
+ tape_buffered_read (ascii_header, in_des, 70L);
+ ascii_header[70] = '\0';
+ sscanf (ascii_header,
+ "%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6lo%11lo",
+ &dev, &file_hdr->c_ino,
+ &file_hdr->c_mode, &file_hdr->c_uid, &file_hdr->c_gid,
+ &file_hdr->c_nlink, &rdev, &file_hdr->c_mtime,
+ &file_hdr->c_namesize, &file_hdr->c_filesize);
+ file_hdr->c_dev_maj = major (dev);
+ file_hdr->c_dev_min = minor (dev);
+ file_hdr->c_rdev_maj = major (rdev);
+ file_hdr->c_rdev_min = minor (rdev);
+
+ /* Read file name from input. */
+ if (file_hdr->c_name != NULL)
+ free (file_hdr->c_name);
+ file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize + 1);
+ tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize);
+#ifndef __MSDOS__
+ /* HP/UX cpio creates archives that look just like ordinary archives,
+ but for devices it sets major = 0, minor = 1, and puts the
+ actual major/minor number in the filesize field. See if this
+ is an HP/UX cpio archive, and if so fix it. We have to do this
+ here because process_copy_in() assumes filesize is always 0
+ for devices. */
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ if (file_hdr->c_filesize != 0
+ && file_hdr->c_rdev_maj == 0
+ && file_hdr->c_rdev_min == 1)
+ {
+ file_hdr->c_rdev_maj = major (file_hdr->c_filesize);
+ file_hdr->c_rdev_min = minor (file_hdr->c_filesize);
+ file_hdr->c_filesize = 0;
+ }
+ break;
+ default:
+ break;
+ }
+#endif /* __MSDOS__ */
+}
+
+/* Fill in FILE_HDR by reading a new-format ASCII format cpio header from
+ file descriptor IN_DES, except for the magic number, which is
+ already filled in. */
+
+void
+read_in_new_ascii (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ char ascii_header[112];
+
+ tape_buffered_read (ascii_header, in_des, 104L);
+ ascii_header[104] = '\0';
+ sscanf (ascii_header,
+ "%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx",
+ &file_hdr->c_ino, &file_hdr->c_mode, &file_hdr->c_uid,
+ &file_hdr->c_gid, &file_hdr->c_nlink, &file_hdr->c_mtime,
+ &file_hdr->c_filesize, &file_hdr->c_dev_maj, &file_hdr->c_dev_min,
+ &file_hdr->c_rdev_maj, &file_hdr->c_rdev_min, &file_hdr->c_namesize,
+ &file_hdr->c_chksum);
+ /* Read file name from input. */
+ if (file_hdr->c_name != NULL)
+ free (file_hdr->c_name);
+ file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize);
+ tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize);
+
+ /* In SVR4 ASCII format, the amount of space allocated for the header
+ is rounded up to the next long-word, so we might need to drop
+ 1-3 bytes. */
+ tape_skip_padding (in_des, file_hdr->c_namesize + 110);
+}
+
+/* Fill in FILE_HDR by reading a binary format cpio header from
+ file descriptor IN_DES, except for the first 6 bytes (the magic
+ number, device, and inode number), which are already filled in. */
+
+void
+read_in_binary (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ struct old_cpio_header short_hdr;
+
+ /* Copy the data into the short header, then later transfer
+ it into the argument long header. */
+ short_hdr.c_dev = ((struct old_cpio_header *) file_hdr)->c_dev;
+ short_hdr.c_ino = ((struct old_cpio_header *) file_hdr)->c_ino;
+ tape_buffered_read (((char *) &short_hdr) + 6, in_des, 20L);
+
+ /* If the magic number is byte swapped, fix the header. */
+ if (file_hdr->c_magic == swab_short ((unsigned short) 070707))
+ {
+ static int warned = 0;
+
+ /* Alert the user that they might have to do byte swapping on
+ the file contents. */
+ if (warned == 0)
+ {
+ error (0, 0, _("warning: archive header has reverse byte-order"));
+ warned = 1;
+ }
+ swab_array ((char *) &short_hdr, 13);
+ }
+
+ file_hdr->c_dev_maj = major (short_hdr.c_dev);
+ file_hdr->c_dev_min = minor (short_hdr.c_dev);
+ file_hdr->c_ino = short_hdr.c_ino;
+ file_hdr->c_mode = short_hdr.c_mode;
+ file_hdr->c_uid = short_hdr.c_uid;
+ file_hdr->c_gid = short_hdr.c_gid;
+ file_hdr->c_nlink = short_hdr.c_nlink;
+ file_hdr->c_rdev_maj = major (short_hdr.c_rdev);
+ file_hdr->c_rdev_min = minor (short_hdr.c_rdev);
+ file_hdr->c_mtime = (unsigned long) short_hdr.c_mtimes[0] << 16
+ | short_hdr.c_mtimes[1];
+
+ file_hdr->c_namesize = short_hdr.c_namesize;
+ file_hdr->c_filesize = (unsigned long) short_hdr.c_filesizes[0] << 16
+ | short_hdr.c_filesizes[1];
+
+ /* Read file name from input. */
+ if (file_hdr->c_name != NULL)
+ free (file_hdr->c_name);
+ file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize);
+ tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize);
+
+ /* In binary mode, the amount of space allocated in the header for
+ the filename is `c_namesize' rounded up to the next short-word,
+ so we might need to drop a byte. */
+ if (file_hdr->c_namesize % 2)
+ tape_toss_input (in_des, 1L);
+
+#ifndef __MSDOS__
+ /* HP/UX cpio creates archives that look just like ordinary archives,
+ but for devices it sets major = 0, minor = 1, and puts the
+ actual major/minor number in the filesize field. See if this
+ is an HP/UX cpio archive, and if so fix it. We have to do this
+ here because process_copy_in() assumes filesize is always 0
+ for devices. */
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ if (file_hdr->c_filesize != 0
+ && file_hdr->c_rdev_maj == 0
+ && file_hdr->c_rdev_min == 1)
+ {
+ file_hdr->c_rdev_maj = major (file_hdr->c_filesize);
+ file_hdr->c_rdev_min = minor (file_hdr->c_filesize);
+ file_hdr->c_filesize = 0;
+ }
+ break;
+ default:
+ break;
+ }
+#endif /* __MSDOS__ */
+}
+
+/* Exchange the bytes of each element of the array of COUNT shorts
+ starting at PTR. */
+
+void
+swab_array (ptr, count)
+ char *ptr;
+ int count;
+{
+ char tmp;
+
+ while (count-- > 0)
+ {
+ tmp = *ptr;
+ *ptr = *(ptr + 1);
+ ++ptr;
+ *ptr = tmp;
+ ++ptr;
+ }
+}
+
+/* Current time for verbose table. */
+static time_t current_time;
+
+/* Read the collection from standard input and create files
+ in the file system. */
+
+void
+process_copy_in ()
+{
+ char done = FALSE; /* True if trailer reached. */
+ FILE *tty_in; /* Interactive file for rename option. */
+ FILE *tty_out; /* Interactive file for rename option. */
+ FILE *rename_in; /* Batch file for rename option. */
+ struct stat file_stat; /* Output file stat record. */
+ struct new_cpio_header file_hdr; /* Output header information. */
+ int in_file_des; /* Input file descriptor. */
+ char skip_file; /* Flag for use with patterns. */
+ int i; /* Loop index variable. */
+
+ /* Initialize the copy in. */
+ if (pattern_file_name)
+ {
+ read_pattern_file ();
+ }
+ file_hdr.c_name = NULL;
+
+ if (rename_batch_file)
+ {
+ rename_in = fopen (rename_batch_file, "r");
+ if (rename_in == NULL)
+ {
+ error (2, errno, CONSOLE);
+ }
+ }
+ else if (rename_flag)
+ {
+ /* Open interactive file pair for rename operation. */
+ tty_in = fopen (CONSOLE, "r");
+ if (tty_in == NULL)
+ {
+ error (2, errno, CONSOLE);
+ }
+ tty_out = fopen (CONSOLE, "w");
+ if (tty_out == NULL)
+ {
+ error (2, errno, CONSOLE);
+ }
+ }
+
+ /* Get date and time if needed for processing the table option. */
+ if (table_flag && verbose_flag)
+ {
+ time (&current_time);
+ }
+
+#ifdef __MSDOS__
+ setmode (archive_des, O_BINARY);
+#endif
+ /* Check whether the input file might be a tape. */
+ in_file_des = archive_des;
+ if (_isrmt (in_file_des))
+ {
+ input_is_special = 1;
+ input_is_seekable = 0;
+ }
+ else
+ {
+ if (fstat (in_file_des, &file_stat))
+ error (1, errno, _("standard input is closed"));
+ input_is_special =
+#ifdef S_ISBLK
+ S_ISBLK (file_stat.st_mode) ||
+#endif
+ S_ISCHR (file_stat.st_mode);
+ input_is_seekable = S_ISREG (file_stat.st_mode);
+ }
+ output_is_seekable = TRUE;
+
+ /* While there is more input in the collection, process the input. */
+ while (!done)
+ {
+ swapping_halfwords = swapping_bytes = FALSE;
+
+ /* Start processing the next file by reading the header. */
+ read_in_header (&file_hdr, in_file_des);
+
+#ifdef DEBUG_CPIO
+ if (debug_flag)
+ {
+ struct new_cpio_header *h;
+ h = &file_hdr;
+ fprintf (stderr,
+ "magic = 0%o, ino = %d, mode = 0%o, uid = %d, gid = %d\n",
+ h->c_magic, h->c_ino, h->c_mode, h->c_uid, h->c_gid);
+ fprintf (stderr,
+ "nlink = %d, mtime = %d, filesize = %d, dev_maj = 0x%x\n",
+ h->c_nlink, h->c_mtime, h->c_filesize, h->c_dev_maj);
+ fprintf (stderr,
+ "dev_min = 0x%x, rdev_maj = 0x%x, rdev_min = 0x%x, namesize = %d\n",
+ h->c_dev_min, h->c_rdev_maj, h->c_rdev_min, h->c_namesize);
+ fprintf (stderr,
+ "chksum = %d, name = \"%s\", tar_linkname = \"%s\"\n",
+ h->c_chksum, h->c_name,
+ h->c_tar_linkname ? h->c_tar_linkname : "(null)" );
+
+ }
+#endif
+ /* Is this the header for the TRAILER file? */
+ if (strcmp ("TRAILER!!!", file_hdr.c_name) == 0)
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Do we have to ignore absolute paths, and if so, does the filename
+ have an absolute path? */
+ if (no_abs_paths_flag && file_hdr.c_name && file_hdr.c_name [0] == '/')
+ {
+ char *p;
+
+ p = file_hdr.c_name;
+ while (*p == '/')
+ ++p;
+ if (*p == '\0')
+ {
+ strcpy (file_hdr.c_name, ".");
+ }
+ else
+ {
+ /* Debian hack: file_hrd.c_name is sometimes set to
+ point to static memory by code in tar.c. This
+ causes a segfault. Therefore, memmove is used
+ instead of freeing and reallocating. (Reported by
+ Horst Knobloch.) This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */
+ (void)memmove (file_hdr.c_name, p, (size_t)(strlen (p) + 1));
+ }
+ }
+
+ /* Does the file name match one of the given patterns? */
+ if (num_patterns <= 0)
+ skip_file = FALSE;
+ else
+ {
+ skip_file = copy_matching_files;
+ for (i = 0; i < num_patterns
+ && skip_file == copy_matching_files; i++)
+ {
+ if (fnmatch (save_patterns[i], file_hdr.c_name, 0) == 0)
+ skip_file = !copy_matching_files;
+ }
+ }
+
+ if (skip_file)
+ {
+ /* If we're skipping a file with links, there might be other
+ links that we didn't skip, and this file might have the
+ data for the links. If it does, we'll copy in the data
+ to the links, but not to this file. */
+ if (file_hdr.c_nlink > 1 && (archive_format == arf_newascii
+ || archive_format == arf_crcascii) )
+ {
+ if (create_defered_links_to_skipped(&file_hdr, in_file_des) < 0)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ }
+ }
+ else
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ }
+ }
+ else if (table_flag)
+ {
+ list_file(&file_hdr, in_file_des);
+ }
+ else if (append_flag)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ }
+ else if (only_verify_crc_flag)
+ {
+#ifdef CP_IFLNK
+ if ((file_hdr.c_mode & CP_IFMT) == CP_IFLNK)
+ {
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ continue;
+ }
+ }
+#endif
+ crc = 0;
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ if (crc != file_hdr.c_chksum)
+ {
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+ file_hdr.c_name, crc, file_hdr.c_chksum);
+ }
+ /* Debian hack: -v and -V now work with --only-verify-crc.
+ (99/11/10) -BEM */
+ if (verbose_flag)
+ {
+ fprintf (stderr, "%s\n", file_hdr.c_name);
+ }
+ if (dot_flag)
+ {
+ fputc ('.', stderr);
+ }
+ }
+ else
+ {
+ /* Copy the input file into the directory structure. */
+
+ /* Do we need to rename the file? */
+ if (rename_flag || rename_batch_file)
+ {
+ if (query_rename(&file_hdr, tty_in, tty_out, rename_in) < 0)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ continue;
+ }
+ }
+
+ copyin_file(&file_hdr, in_file_des);
+
+ if (verbose_flag)
+ fprintf (stderr, "%s\n", file_hdr.c_name);
+ if (dot_flag)
+ fputc ('.', stderr);
+ }
+ }
+
+ if (dot_flag)
+ fputc ('\n', stderr);
+
+ if (append_flag)
+ return;
+
+ if (archive_format == arf_newascii || archive_format == arf_crcascii)
+ {
+ create_final_defers ();
+ }
+ if (!quiet_flag)
+ {
+ int blocks;
+ blocks = (input_bytes + io_block_size - 1) / io_block_size;
+ fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", blocks), blocks);
+ }
+}
+
+static int
+query_rename(file_hdr, tty_in, tty_out, rename_in)
+ struct new_cpio_header* file_hdr;
+ FILE *tty_in;
+ FILE *tty_out;
+ FILE *rename_in;
+{
+ char *str_res; /* Result for string function. */
+ static dynamic_string new_name; /* New file name for rename option. */
+ static int initialized_new_name = FALSE;
+ if (!initialized_new_name)
+ {
+ ds_init (&new_name, 128);
+ initialized_new_name = TRUE;
+ }
+
+ if (rename_flag)
+ {
+ fprintf (tty_out, _("rename %s -> "), file_hdr->c_name);
+ fflush (tty_out);
+ str_res = ds_fgets (tty_in, &new_name);
+ }
+ else
+ {
+ str_res = ds_fgetstr (rename_in, &new_name, '\n');
+ }
+ if (str_res == NULL || str_res[0] == 0)
+ {
+ return -1;
+ }
+ else
+ /* Debian hack: file_hrd.c_name is sometimes set to
+ point to static memory by code in tar.c. This
+ causes a segfault. This has been fixed and an
+ additional check to ensure that the file name
+ is not too long has been added. (Reported by
+ Horst Knobloch.) This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */
+ {
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ {
+ free (file_hdr->c_name);
+ file_hdr->c_name = xstrdup (new_name.ds_string);
+ }
+ else
+ {
+ if (is_tar_filename_too_long (new_name.ds_string))
+ error (0, 0, _("%s: file name too long"),
+ new_name.ds_string);
+ else
+ strcpy (file_hdr->c_name, new_name.ds_string);
+ }
+ }
+ return 0;
+}
+
+static void
+list_file(file_hdr, in_file_des)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+{
+ if (verbose_flag)
+ {
+#ifdef CP_IFLNK
+ if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
+ {
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ {
+ char *link_name = NULL; /* Name of hard and symbolic links. */
+
+ link_name = (char *) xmalloc ((unsigned int) file_hdr->c_filesize + 1);
+ link_name[file_hdr->c_filesize] = '\0';
+ tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize);
+ long_format (file_hdr, link_name);
+ free (link_name);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ else
+ {
+ long_format (file_hdr, file_hdr->c_tar_linkname);
+ return;
+ }
+ }
+ else
+#endif
+ long_format (file_hdr, (char *) 0);
+ }
+ else
+ {
+ /* Debian hack: Modified to print a list of filenames
+ terminiated by a null character when the -t and -0
+ flags are used. This has been submitted as a
+ suggestion to "bug-gnu-utils@prep.ai.mit.edu". -BEM */
+ printf ("%s%c", file_hdr->c_name, name_end);
+ }
+
+ crc = 0;
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ if (only_verify_crc_flag)
+ {
+#ifdef CP_IFLNK
+ if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
+ {
+ return; /* links don't have a checksum */
+ }
+#endif
+ if (crc != file_hdr->c_chksum)
+ {
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+ file_hdr->c_name, crc, file_hdr->c_chksum);
+ }
+ }
+}
+
+static void
+copyin_file(file_hdr, in_file_des)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+{
+ int existing_dir;
+
+ if (try_existing_file(file_hdr, in_file_des, &existing_dir) < 0)
+ {
+ return;
+ }
+
+ /* Do the real copy or link. */
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFREG:
+ copyin_regular_file(file_hdr, in_file_des);
+ break;
+
+ case CP_IFDIR:
+ copyin_directory(file_hdr, existing_dir);
+ break;
+
+#ifndef __MSDOS__
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ copyin_device(file_hdr);
+ break;
+#endif
+
+#ifdef CP_IFLNK
+ case CP_IFLNK:
+ copyin_link(file_hdr, in_file_des);
+ break;
+#endif
+
+ default:
+ error (0, 0, _("%s: unknown file type"), file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ }
+}
+
+static int
+try_existing_file(file_hdr, in_file_des, existing_dir)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+ int* existing_dir;
+{
+ struct stat file_stat;
+
+ *existing_dir = FALSE;
+ if (lstat (file_hdr->c_name, &file_stat) == 0)
+ {
+ if (S_ISDIR (file_stat.st_mode)
+ && ((file_hdr->c_mode & CP_IFMT) == CP_IFDIR))
+ {
+ /* If there is already a directory there that
+ we are trying to create, don't complain about
+ it. */
+ *existing_dir = TRUE;
+ return 0;
+ }
+ else if (!unconditional_flag
+ && file_hdr->c_mtime <= file_stat.st_mtime)
+ {
+ error (0, 0, _("%s not created: newer or same age version exists"),
+ file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return -1; /* Go to the next file. */
+ }
+ else if (S_ISDIR (file_stat.st_mode)
+ ? rmdir (file_hdr->c_name)
+ : unlink (file_hdr->c_name))
+ {
+ error (0, errno, _("cannot remove current %s"),
+ file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return -1; /* Go to the next file. */
+ }
+ }
+ return 0;
+}
+
+static void
+copyin_regular_file(file_hdr, in_file_des)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+{
+ int out_file_des; /* Output file descriptor. */
+
+#ifndef __MSDOS__
+ /* Can the current file be linked to a previously copied file? */
+ if (file_hdr->c_nlink > 1 && (archive_format == arf_newascii
+ || archive_format == arf_crcascii) )
+ {
+ int link_res;
+ if (file_hdr->c_filesize == 0)
+ {
+ /* The newc and crc formats store multiply linked copies
+ of the same file in the archive only once. The
+ actual data is attached to the last link in the
+ archive, and the other links all have a filesize
+ of 0. Since this file has multiple links and a
+ filesize of 0, its data is probably attatched to
+ another file in the archive. Save the link, and
+ process it later when we get the actual data. We
+ can't just create it with length 0 and add the
+ data later, in case the file is readonly. We still
+ lose if its parent directory is readonly (and we aren't
+ running as root), but there's nothing we can do about
+ that. */
+ defer_copyin (file_hdr);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ /* If the file has data (filesize != 0), then presumably
+ any other links have already been defer_copyin'ed(),
+ but GNU cpio version 2.0-2.2 didn't do that, so we
+ still have to check for links here (and also in case
+ the archive was created and later appeneded to). */
+ /* Debian hack: (97/1/2) This was reported by Ronald
+ F. Guilmette to the upstream maintainers. -BEM */
+ link_res = link_to_maj_min_ino (file_hdr->c_name,
+ file_hdr->c_dev_maj, file_hdr->c_dev_min,
+ file_hdr->c_ino);
+ if (link_res == 0)
+ {
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ }
+ else if (file_hdr->c_nlink > 1 && archive_format != arf_tar
+ && archive_format != arf_ustar)
+ {
+ int link_res;
+ /* Debian hack: (97/1/2) This was reported by Ronald
+ F. Guilmette to the upstream maintainers. -BEM */
+ link_res = link_to_maj_min_ino (file_hdr->c_name,
+ file_hdr->c_dev_maj, file_hdr->c_dev_min,
+ file_hdr->c_ino);
+ if (link_res == 0)
+ {
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ }
+ else if ((archive_format == arf_tar || archive_format == arf_ustar)
+ && file_hdr->c_tar_linkname &&
+ file_hdr->c_tar_linkname[0] != '\0')
+ {
+ int link_res;
+ link_res = link_to_name (file_hdr->c_name,
+ file_hdr->c_tar_linkname);
+ if (link_res < 0)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+ file_hdr->c_tar_linkname, file_hdr->c_name);
+ }
+ return;
+ }
+#endif
+
+ /* If not linked, copy the contents of the file. */
+ out_file_des = open (file_hdr->c_name,
+ O_CREAT | O_WRONLY | O_BINARY, 0600);
+ if (out_file_des < 0 && create_dir_flag)
+ {
+ create_all_directories (file_hdr->c_name);
+ out_file_des = open (file_hdr->c_name,
+ O_CREAT | O_WRONLY | O_BINARY,
+ 0600);
+ }
+ if (out_file_des < 0)
+ {
+ error (0, errno, "%s", file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+
+ crc = 0;
+ if (swap_halfwords_flag)
+ {
+ if ((file_hdr->c_filesize % 4) == 0)
+ swapping_halfwords = TRUE;
+ else
+ error (0, 0, _("cannot swap halfwords of %s: odd number of halfwords"),
+ file_hdr->c_name);
+ }
+ if (swap_bytes_flag)
+ {
+ if ((file_hdr->c_filesize % 2) == 0)
+ swapping_bytes = TRUE;
+ else
+ error (0, 0, _("cannot swap bytes of %s: odd number of bytes"),
+ file_hdr->c_name);
+ }
+ copy_files_tape_to_disk (in_file_des, out_file_des, file_hdr->c_filesize);
+ disk_empty_output_buffer (out_file_des);
+ /* Debian hack to fix a bug in the --sparse option.
+ This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
+ if (delayed_seek_count > 0)
+ {
+ lseek (out_file_des, delayed_seek_count-1, SEEK_CUR);
+ write (out_file_des, "", 1);
+ delayed_seek_count = 0;
+ }
+ if (close (out_file_des) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+
+ if (archive_format == arf_crcascii)
+ {
+ if (crc != file_hdr->c_chksum)
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+ file_hdr->c_name, crc, file_hdr->c_chksum);
+ }
+ /* File is now copied; set attributes. */
+ if (!no_chown_flag)
+ if ((chown (file_hdr->c_name,
+ set_owner_flag ? set_owner : file_hdr->c_uid,
+ set_group_flag ? set_group : file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", file_hdr->c_name);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (file_hdr->c_name, (int) file_hdr->c_mode) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ if (retain_time_flag)
+ {
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ times.actime = times.modtime = file_hdr->c_mtime;
+ if (utime (file_hdr->c_name, &times) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ }
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ if (file_hdr->c_nlink > 1 && (archive_format == arf_newascii
+ || archive_format == arf_crcascii) )
+ {
+ /* (see comment above for how the newc and crc formats
+ store multiple links). Now that we have the data
+ for this file, create any other links to it which
+ we defered. */
+ create_defered_links (file_hdr);
+ }
+}
+
+static void
+copyin_directory(file_hdr, existing_dir)
+ struct new_cpio_header* file_hdr;
+ int existing_dir;
+{
+ int res; /* Result of various function calls. */
+#ifdef HPUX_CDF
+ int cdf_flag; /* True if file is a CDF. */
+ int cdf_char; /* Index of `+' char indicating a CDF. */
+#endif
+
+ /* Strip any trailing `/'s off the filename; tar puts
+ them on. We might as well do it here in case anybody
+ else does too, since they cause strange things to happen. */
+ strip_trailing_slashes (file_hdr->c_name);
+
+ /* Ignore the current directory. It must already exist,
+ and we don't want to change its permission, ownership
+ or time. */
+ if (file_hdr->c_name[0] == '.' && file_hdr->c_name[1] == '\0')
+ {
+ return;
+ }
+
+#ifdef HPUX_CDF
+ cdf_flag = 0;
+#endif
+ if (!existing_dir)
+
+ {
+#ifdef HPUX_CDF
+ /* If the directory name ends in a + and is SUID,
+ then it is a CDF. Strip the trailing + from
+ the name before creating it. */
+ cdf_char = strlen (file_hdr->c_name) - 1;
+ if ( (cdf_char > 0) &&
+ (file_hdr->c_mode & 04000) &&
+ (file_hdr->c_name [cdf_char] == '+') )
+ {
+ file_hdr->c_name [cdf_char] = '\0';
+ cdf_flag = 1;
+ }
+#endif
+ res = mkdir (file_hdr->c_name, file_hdr->c_mode);
+ }
+ else
+ res = 0;
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (file_hdr->c_name);
+ res = mkdir (file_hdr->c_name, file_hdr->c_mode);
+ }
+ if (res < 0)
+ {
+ /* In some odd cases where the file_hdr->c_name includes `.',
+ the directory may have actually been created by
+ create_all_directories(), so the mkdir will fail
+ because the directory exists. If that's the case,
+ don't complain about it. */
+ struct stat file_stat;
+ if ( (errno != EEXIST) ||
+ (lstat (file_hdr->c_name, &file_stat) != 0) ||
+ !(S_ISDIR (file_stat.st_mode) ) )
+ {
+ error (0, errno, "%s", file_hdr->c_name);
+ return;
+ }
+ }
+ if (!no_chown_flag)
+ if ((chown (file_hdr->c_name,
+ set_owner_flag ? set_owner : file_hdr->c_uid,
+ set_group_flag ? set_group : file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", file_hdr->c_name);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (file_hdr->c_name, (int) file_hdr->c_mode) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+#ifdef HPUX_CDF
+ if (cdf_flag)
+ /* Once we "hide" the directory with the chmod(),
+ we have to refer to it using name+ instead of name. */
+ file_hdr->c_name [cdf_char] = '+';
+#endif
+ if (retain_time_flag)
+ {
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ times.actime = times.modtime = file_hdr->c_mtime;
+ if (utime (file_hdr->c_name, &times) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ }
+}
+
+static void
+copyin_device(file_hdr)
+ struct new_cpio_header* file_hdr;
+{
+ int res; /* Result of various function calls. */
+
+ if (file_hdr->c_nlink > 1 && archive_format != arf_tar
+ && archive_format != arf_ustar)
+ {
+ int link_res;
+ /* Debian hack: This was reported by Horst