Blame src/ar.c

Packit Service 97d2fb
/* Create, modify, and extract from archives.
Packit Service 97d2fb
   Copyright (C) 2005-2012, 2016, 2017 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
   Written by Ulrich Drepper <drepper@redhat.com>, 2005.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of the GNU General Public License as published by
Packit Service 97d2fb
   the Free Software Foundation; either version 3 of the License, or
Packit Service 97d2fb
   (at your option) any later version.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 97d2fb
   GNU General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received a copy of the GNU General Public License
Packit Service 97d2fb
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include <argp.h>
Packit Service 97d2fb
#include <assert.h>
Packit Service 97d2fb
#include <fcntl.h>
Packit Service 97d2fb
#include <gelf.h>
Packit Service 97d2fb
#include <libintl.h>
Packit Service 97d2fb
#include <limits.h>
Packit Service 97d2fb
#include <locale.h>
Packit Service 97d2fb
#include <search.h>
Packit Service 97d2fb
#include <stdbool.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <stdio.h>
Packit Service 97d2fb
#include <stdio_ext.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
#include <time.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
#include <sys/mman.h>
Packit Service 97d2fb
#include <sys/stat.h>
Packit Service 97d2fb
#include <sys/time.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include <system.h>
Packit Service 97d2fb
#include <printversion.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include "arlib.h"
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Name and version of program.  */
Packit Service 97d2fb
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
Packit Service 97d2fb
Packit Service 97d2fb
/* Prototypes for local functions.  */
Packit Service 97d2fb
static int do_oper_extract (int oper, const char *arfname, char **argv,
Packit Service 97d2fb
			    int argc, long int instance);
Packit Service 97d2fb
static int do_oper_delete (const char *arfname, char **argv, int argc,
Packit Service 97d2fb
			   long int instance);
Packit Service 97d2fb
static int do_oper_insert (int oper, const char *arfname, char **argv,
Packit Service 97d2fb
			   int argc, const char *member);
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Bug report address.  */
Packit Service 97d2fb
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Definitions of arguments for argp functions.  */
Packit Service 97d2fb
static const struct argp_option options[] =
Packit Service 97d2fb
{
Packit Service 97d2fb
  { NULL, 0, NULL, 0, N_("Commands:"), 1 },
Packit Service 97d2fb
  { NULL, 'd', NULL, 0, N_("Delete files from archive."), 0 },
Packit Service 97d2fb
  { NULL, 'm', NULL, 0, N_("Move files in archive."), 0 },
Packit Service 97d2fb
  { NULL, 'p', NULL, 0, N_("Print files in archive."), 0 },
Packit Service 97d2fb
  { NULL, 'q', NULL, 0, N_("Quick append files to archive."), 0 },
Packit Service 97d2fb
  { NULL, 'r', NULL, 0,
Packit Service 97d2fb
    N_("Replace existing or insert new file into archive."), 0 },
Packit Service 97d2fb
  { NULL, 't', NULL, 0, N_("Display content of archive."), 0 },
Packit Service 97d2fb
  { NULL, 'x', NULL, 0, N_("Extract files from archive."), 0 },
Packit Service 97d2fb
Packit Service 97d2fb
  { NULL, 0, NULL, 0, N_("Command Modifiers:"), 2 },
Packit Service 97d2fb
  { NULL, 'o', NULL, 0, N_("Preserve original dates."), 0 },
Packit Service 97d2fb
  { NULL, 'N', NULL, 0, N_("Use instance [COUNT] of name."), 0 },
Packit Service 97d2fb
  { NULL, 'C', NULL, 0,
Packit Service 97d2fb
    N_("Do not replace existing files with extracted files."), 0 },
Packit Service 97d2fb
  { NULL, 'T', NULL, 0, N_("Allow filename to be truncated if necessary."),
Packit Service 97d2fb
    0 },
Packit Service 97d2fb
  { NULL, 'v', NULL, 0, N_("Provide verbose output."), 0 },
Packit Service 97d2fb
  { NULL, 's', NULL, 0, N_("Force regeneration of symbol table."), 0 },
Packit Service 97d2fb
  { NULL, 'a', NULL, 0, N_("Insert file after [MEMBER]."), 0 },
Packit Service 97d2fb
  { NULL, 'b', NULL, 0, N_("Insert file before [MEMBER]."), 0 },
Packit Service 97d2fb
  { NULL, 'i', NULL, 0, N_("Same as -b."), 0 },
Packit Service 97d2fb
  { NULL, 'c', NULL, 0, N_("Suppress message when library has to be created."),
Packit Service 97d2fb
    0 },
Packit Service 97d2fb
  { NULL, 'P', NULL, 0, N_("Use full path for file matching."), 0 },
Packit Service 97d2fb
  { NULL, 'u', NULL, 0, N_("Update only older files in archive."), 0 },
Packit Service 97d2fb
Packit Service 97d2fb
  { NULL, 0, NULL, 0, NULL, 0 }
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
/* Short description of program.  */
Packit Service 97d2fb
static const char doc[] = N_("Create, modify, and extract from archives.");
Packit Service 97d2fb
Packit Service 97d2fb
/* Strings for arguments in help texts.  */
Packit Service 97d2fb
static const char args_doc[] = N_("[MEMBER] [COUNT] ARCHIVE [FILE...]");
Packit Service 97d2fb
Packit Service 97d2fb
/* Prototype for option handler.  */
Packit Service 97d2fb
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit Service 97d2fb
Packit Service 97d2fb
/* Data structure to communicate with argp functions.  */
Packit Service 97d2fb
static struct argp argp =
Packit Service 97d2fb
{
Packit Service 97d2fb
  options, parse_opt, args_doc, doc, arlib_argp_children, NULL, NULL
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* What operation to perform.  */
Packit Service 97d2fb
static enum
Packit Service 97d2fb
  {
Packit Service 97d2fb
    oper_none,
Packit Service 97d2fb
    oper_delete,
Packit Service 97d2fb
    oper_move,
Packit Service 97d2fb
    oper_print,
Packit Service 97d2fb
    oper_qappend,
Packit Service 97d2fb
    oper_replace,
Packit Service 97d2fb
    oper_list,
Packit Service 97d2fb
    oper_extract
Packit Service 97d2fb
  } operation;
Packit Service 97d2fb
Packit Service 97d2fb
/* Modifiers.  */
Packit Service 97d2fb
static bool verbose;
Packit Service 97d2fb
static bool preserve_dates;
Packit Service 97d2fb
static bool instance_specifed;
Packit Service 97d2fb
static bool dont_replace_existing;
Packit Service 97d2fb
static bool allow_truncate_fname;
Packit Service 97d2fb
static bool force_symtab;
Packit Service 97d2fb
static bool suppress_create_msg;
Packit Service 97d2fb
static bool full_path;
Packit Service 97d2fb
static bool update_newer;
Packit Service 97d2fb
static enum { ipos_none, ipos_before, ipos_after } ipos;
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
main (int argc, char *argv[])
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* We use no threads here which can interfere with handling a stream.  */
Packit Service 97d2fb
  (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
Packit Service 97d2fb
  (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
Packit Service 97d2fb
  (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Set locale.  */
Packit Service 97d2fb
  (void) setlocale (LC_ALL, "");
Packit Service 97d2fb
Packit Service 97d2fb
  /* Make sure the message catalog can be found.  */
Packit Service 97d2fb
  (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Initialize the message catalog.  */
Packit Service 97d2fb
  (void) textdomain (PACKAGE_TARNAME);
Packit Service 97d2fb
Packit Service 97d2fb
  /* For historical reasons the options in the first parameter need
Packit Service 97d2fb
     not be preceded by a dash.  Add it now if necessary.  */
Packit Service 97d2fb
  if (argc > 1 && argv[1][0] != '-')
Packit Service 97d2fb
    {
Packit Service 97d2fb
      size_t len = strlen (argv[1]) + 1;
Packit Service 97d2fb
      char *newp = alloca (len + 1);
Packit Service 97d2fb
      newp[0] = '-';
Packit Service 97d2fb
      memcpy (&newp[1], argv[1], len);
Packit Service 97d2fb
      argv[1] = newp;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Parse and process arguments.  */
Packit Service 97d2fb
  int remaining;
Packit Service 97d2fb
  (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Tell the library which version we are expecting.  */
Packit Service 97d2fb
  (void) elf_version (EV_CURRENT);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Handle the [MEMBER] parameter.  */
Packit Service 97d2fb
  const char *member = NULL;
Packit Service 97d2fb
  if (ipos != ipos_none)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Only valid for certain operations.  */
Packit Service 97d2fb
      if (operation != oper_move && operation != oper_replace)
Packit Service 97d2fb
	error (1, 0, gettext ("\
Packit Service 97d2fb
'a', 'b', and 'i' are only allowed with the 'm' and 'r' options"));
Packit Service 97d2fb
Packit Service 97d2fb
      if (remaining == argc)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  error (0, 0, gettext ("\
Packit Service 97d2fb
MEMBER parameter required for 'a', 'b', and 'i' modifiers"));
Packit Service 97d2fb
	  argp_help (&argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_SEE,
Packit Service 97d2fb
		     program_invocation_short_name);
Packit Service 97d2fb
	  exit (EXIT_FAILURE);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      member = argv[remaining++];
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Handle the [COUNT] parameter.  */
Packit Service 97d2fb
  long int instance = -1;
Packit Service 97d2fb
  if (instance_specifed)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Only valid for certain operations.  */
Packit Service 97d2fb
      if (operation != oper_extract && operation != oper_delete)
Packit Service 97d2fb
	error (1, 0, gettext ("\
Packit Service 97d2fb
'N' is only meaningful with the 'x' and 'd' options"));
Packit Service 97d2fb
Packit Service 97d2fb
      if (remaining == argc)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  error (0, 0, gettext ("COUNT parameter required"));
Packit Service 97d2fb
	  argp_help (&argp, stderr, ARGP_HELP_SEE,
Packit Service 97d2fb
		     program_invocation_short_name);
Packit Service 97d2fb
	  exit (EXIT_FAILURE);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      char *endp;
Packit Service 97d2fb
      errno = 0;
Packit Service 97d2fb
      if (((instance = strtol (argv[remaining], &endp, 10)) == LONG_MAX
Packit Service 97d2fb
	   && errno == ERANGE)
Packit Service 97d2fb
	  || instance <= 0
Packit Service 97d2fb
	  || *endp != '\0')
Packit Service 97d2fb
	error (1, 0, gettext ("invalid COUNT parameter %s"), argv[remaining]);
Packit Service 97d2fb
Packit Service 97d2fb
      ++remaining;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if ((dont_replace_existing || allow_truncate_fname)
Packit Service 97d2fb
      && unlikely (operation != oper_extract))
Packit Service 97d2fb
    error (1, 0, gettext ("'%c' is only meaningful with the 'x' option"),
Packit Service 97d2fb
	   dont_replace_existing ? 'C' : 'T');
Packit Service 97d2fb
Packit Service 97d2fb
  /* There must at least be one more parameter specifying the archive.   */
Packit Service 97d2fb
  if (remaining == argc)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      error (0, 0, gettext ("archive name required"));
Packit Service 97d2fb
      argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
Packit Service 97d2fb
      exit (EXIT_FAILURE);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  const char *arfname = argv[remaining++];
Packit Service 97d2fb
  argv += remaining;
Packit Service 97d2fb
  argc -= remaining;
Packit Service 97d2fb
Packit Service 97d2fb
  int status;
Packit Service 97d2fb
  switch (operation)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case oper_none:
Packit Service 97d2fb
      error (0, 0, gettext ("command option required"));
Packit Service 97d2fb
      argp_help (&argp, stderr, ARGP_HELP_STD_ERR,
Packit Service 97d2fb
		 program_invocation_short_name);
Packit Service 97d2fb
      status = 1;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case oper_list:
Packit Service 97d2fb
    case oper_print:
Packit Service 97d2fb
      status = do_oper_extract (operation, arfname, argv, argc, -1);
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case oper_extract:
Packit Service 97d2fb
      status = do_oper_extract (operation, arfname, argv, argc, instance);
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case oper_delete:
Packit Service 97d2fb
      status = do_oper_delete (arfname, argv, argc, instance);
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case oper_move:
Packit Service 97d2fb
    case oper_qappend:
Packit Service 97d2fb
    case oper_replace:
Packit Service 97d2fb
      status = do_oper_insert (operation, arfname, argv, argc, member);
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    default:
Packit Service 97d2fb
      assert (! "should not happen");
Packit Service 97d2fb
      status = 1;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return status;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Handle program arguments.  */
Packit Service 97d2fb
static error_t
Packit Service 97d2fb
parse_opt (int key, char *arg __attribute__ ((unused)),
Packit Service 97d2fb
	   struct argp_state *state __attribute__ ((unused)))
Packit Service 97d2fb
{
Packit Service 97d2fb
  switch (key)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case 'd':
Packit Service 97d2fb
    case 'm':
Packit Service 97d2fb
    case 'p':
Packit Service 97d2fb
    case 'q':
Packit Service 97d2fb
    case 'r':
Packit Service 97d2fb
    case 't':
Packit Service 97d2fb
    case 'x':
Packit Service 97d2fb
      if (operation != oper_none)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  error (0, 0, gettext ("More than one operation specified"));
Packit Service 97d2fb
	  argp_help (&argp, stderr, ARGP_HELP_SEE,
Packit Service 97d2fb
		     program_invocation_short_name);
Packit Service 97d2fb
	  exit (EXIT_FAILURE);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      switch (key)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	case 'd':
Packit Service 97d2fb
	  operation = oper_delete;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case 'm':
Packit Service 97d2fb
	  operation = oper_move;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case 'p':
Packit Service 97d2fb
	  operation = oper_print;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case 'q':
Packit Service 97d2fb
	  operation = oper_qappend;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case 'r':
Packit Service 97d2fb
	  operation = oper_replace;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case 't':
Packit Service 97d2fb
	  operation = oper_list;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case 'x':
Packit Service 97d2fb
	  operation = oper_extract;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'a':
Packit Service 97d2fb
      ipos = ipos_after;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'b':
Packit Service 97d2fb
    case 'i':
Packit Service 97d2fb
      ipos = ipos_before;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'c':
Packit Service 97d2fb
      suppress_create_msg = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'C':
Packit Service 97d2fb
      dont_replace_existing = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'N':
Packit Service 97d2fb
      instance_specifed = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'o':
Packit Service 97d2fb
      preserve_dates = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'P':
Packit Service 97d2fb
      full_path = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 's':
Packit Service 97d2fb
      force_symtab = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'T':
Packit Service 97d2fb
      allow_truncate_fname = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'u':
Packit Service 97d2fb
      update_newer = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'v':
Packit Service 97d2fb
      verbose = true;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    default:
Packit Service 97d2fb
      return ARGP_ERR_UNKNOWN;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
open_archive (const char *arfname, int flags, int mode, Elf **elf,
Packit Service 97d2fb
	      struct stat *st, bool miss_allowed)
Packit Service 97d2fb
{
Packit Service 97d2fb
  int fd = open (arfname, flags, mode);
Packit Service 97d2fb
  if (fd == -1)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (miss_allowed)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
Packit Service 97d2fb
      error (EXIT_FAILURE, errno, gettext ("cannot open archive '%s'"),
Packit Service 97d2fb
	     arfname);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (elf != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Elf_Cmd cmd = flags == O_RDONLY ? ELF_C_READ_MMAP : ELF_C_RDWR_MMAP;
Packit Service 97d2fb
Packit Service 97d2fb
      *elf = elf_begin (fd, cmd, NULL);
Packit Service 97d2fb
      if (*elf == NULL)
Packit Service 97d2fb
	error (EXIT_FAILURE, 0, gettext ("cannot open archive '%s': %s"),
Packit Service 97d2fb
	       arfname, elf_errmsg (-1));
Packit Service 97d2fb
Packit Service 97d2fb
      if (flags == O_RDONLY && elf_kind (*elf) != ELF_K_AR)
Packit Service 97d2fb
	error (EXIT_FAILURE, 0, gettext ("%s: not an archive file"), arfname);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (st != NULL && fstat (fd, st) != 0)
Packit Service 97d2fb
    error (EXIT_FAILURE, errno, gettext ("cannot stat archive '%s'"),
Packit Service 97d2fb
	   arfname);
Packit Service 97d2fb
Packit Service 97d2fb
  return fd;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static void
Packit Service 97d2fb
not_found (int argc, char *argv[argc], bool found[argc])
Packit Service 97d2fb
{
Packit Service 97d2fb
  for (int i = 0; i < argc; ++i)
Packit Service 97d2fb
    if (!found[i])
Packit Service 97d2fb
      printf (gettext ("no entry %s in archive\n"), argv[i]);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
copy_content (Elf *elf, int newfd, off_t off, size_t n)
Packit Service 97d2fb
{
Packit Service 97d2fb
  size_t len;
Packit Service 97d2fb
  char *rawfile = elf_rawfile (elf, &len;;
Packit Service 97d2fb
Packit Service 97d2fb
  assert (off + n <= len);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Tell the kernel we will read all the pages sequentially.  */
Packit Service 97d2fb
  size_t ps = sysconf (_SC_PAGESIZE);
Packit Service 97d2fb
  if (n > 2 * ps)
Packit Service 97d2fb
    posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL);
Packit Service 97d2fb
Packit Service 97d2fb
  return write_retry (newfd, rawfile + off, n) != (ssize_t) n;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
do_oper_extract (int oper, const char *arfname, char **argv, int argc,
Packit Service 97d2fb
		 long int instance)
Packit Service 97d2fb
{
Packit Service 97d2fb
  bool found[argc > 0 ? argc : 1];
Packit Service 97d2fb
  memset (found, '\0', sizeof (found));
Packit Service 97d2fb
Packit Service 97d2fb
  size_t name_max = 0;
Packit Service 97d2fb
  inline bool should_truncate_fname (void)
Packit Service 97d2fb
  {
Packit Service 97d2fb
    if (errno == ENAMETOOLONG && allow_truncate_fname)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	if (name_max == 0)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    long int len = pathconf (".", _PC_NAME_MAX);
Packit Service 97d2fb
	    if (len > 0)
Packit Service 97d2fb
	      name_max = len;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	return name_max != 0;
Packit Service 97d2fb
      }
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  }
Packit Service 97d2fb
Packit Service 97d2fb
  off_t index_off = -1;
Packit Service 97d2fb
  size_t index_size = 0;
Packit Service 97d2fb
  off_t cur_off = SARMAG;
Packit Service 97d2fb
Packit Service 97d2fb
  int status = 0;
Packit Service 97d2fb
  Elf *elf;
Packit Service 97d2fb
  int fd = open_archive (arfname, O_RDONLY, 0, &elf, NULL, false);
Packit Service 97d2fb
Packit Service 97d2fb
  if (hcreate (2 * argc) == 0)
Packit Service 97d2fb
    error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
Packit Service 97d2fb
Packit Service 97d2fb
  for (int cnt = 0; cnt < argc; ++cnt)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
Packit Service 97d2fb
      if (hsearch (entry, ENTER) == NULL)
Packit Service 97d2fb
	error (EXIT_FAILURE, errno,
Packit Service 97d2fb
	       gettext ("cannot insert into hash table"));
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  struct stat st;
Packit Service 97d2fb
  if (force_symtab)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (fstat (fd, &st) != 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  error (0, errno, gettext ("cannot stat '%s'"), arfname);
Packit Service 97d2fb
	  close (fd);
Packit Service 97d2fb
	  return 1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      arlib_init ();
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Elf_Cmd cmd = ELF_C_READ_MMAP;
Packit Service 97d2fb
  Elf *subelf;
Packit Service 97d2fb
  while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Elf_Arhdr *arhdr = elf_getarhdr (subelf);
Packit Service 97d2fb
Packit Service 97d2fb
      if (strcmp (arhdr->ar_name, "/") == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  index_off = elf_getaroff (subelf);
Packit Service 97d2fb
	  index_size = arhdr->ar_size;
Packit Service 97d2fb
	  goto next;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      if (strcmp (arhdr->ar_name, "//") == 0)
Packit Service 97d2fb
	goto next;
Packit Service 97d2fb
Packit Service 97d2fb
      if (force_symtab)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  arlib_add_symbols (elf, arfname, arhdr->ar_name, cur_off);
Packit Service 97d2fb
	  cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
Packit Service 97d2fb
		      + sizeof (struct ar_hdr));
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      bool do_extract = argc <= 0;
Packit Service 97d2fb
      if (!do_extract)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  ENTRY entry;
Packit Service 97d2fb
	  entry.key = arhdr->ar_name;
Packit Service 97d2fb
	  ENTRY *res = hsearch (entry, FIND);
Packit Service 97d2fb
	  if (res != NULL && (instance < 0 || instance-- == 0)
Packit Service 97d2fb
	      && !found[(char **) res->data - argv])
Packit Service 97d2fb
	    found[(char **) res->data - argv] = do_extract = true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (do_extract)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (verbose)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      if (oper == oper_print)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  printf ("\n<%s>\n\n", arhdr->ar_name);
Packit Service 97d2fb
Packit Service 97d2fb
		  /* We have to flush now because now we use the descriptor
Packit Service 97d2fb
		     directly.  */
Packit Service 97d2fb
		  fflush (stdout);
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      else if (oper == oper_list)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  char datestr[100];
Packit Service 97d2fb
		  struct tm *tp = localtime (&arhdr->ar_date);
Packit Service 97d2fb
		  if (tp == NULL)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      time_t time = 0;
Packit Service 97d2fb
		      tp = localtime (&time);
Packit Service 97d2fb
		    }
Packit Service 97d2fb
Packit Service 97d2fb
		  strftime (datestr, sizeof (datestr), "%b %e %H:%M %Y", tp);
Packit Service 97d2fb
Packit Service 97d2fb
		  printf ("%c%c%c%c%c%c%c%c%c %u/%u %6ju %s %s\n",
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IRUSR) ? 'r' : '-',
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IWUSR) ? 'w' : '-',
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IXUSR)
Packit Service 97d2fb
			  ? ((arhdr->ar_mode & S_ISUID) ? 's' : 'x')
Packit Service 97d2fb
			  : ((arhdr->ar_mode & S_ISUID) ? 'S' : '-'),
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IRGRP) ? 'r' : '-',
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IWGRP) ? 'w' : '-',
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IXGRP)
Packit Service 97d2fb
			  ? ((arhdr->ar_mode & S_ISGID) ? 's' : 'x')
Packit Service 97d2fb
			  : ((arhdr->ar_mode & S_ISGID) ? 'S' : '-'),
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IROTH) ? 'r' : '-',
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IWOTH) ? 'w' : '-',
Packit Service 97d2fb
			  (arhdr->ar_mode & S_IXOTH)
Packit Service 97d2fb
			  ? ((arhdr->ar_mode & S_ISVTX) ? 't' : 'x')
Packit Service 97d2fb
			  : ((arhdr->ar_mode & S_ISVTX) ? 'T' : '-'),
Packit Service 97d2fb
			  arhdr->ar_uid,
Packit Service 97d2fb
			  arhdr->ar_gid,
Packit Service 97d2fb
			  (uintmax_t) arhdr->ar_size,
Packit Service 97d2fb
			  datestr,
Packit Service 97d2fb
			  arhdr->ar_name);
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      else
Packit Service 97d2fb
		printf ("x - %s\n", arhdr->ar_name);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  if (oper == oper_list)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      if (!verbose)
Packit Service 97d2fb
		puts (arhdr->ar_name);
Packit Service 97d2fb
Packit Service 97d2fb
	      goto next;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  size_t nleft;
Packit Service 97d2fb
	  char *data = elf_rawfile (subelf, &nleft);
Packit Service 97d2fb
	  if (data == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      error (0, 0, gettext ("cannot read content of %s: %s"),
Packit Service 97d2fb
		     arhdr->ar_name, elf_errmsg (-1));
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	      goto next;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  int xfd;
Packit Service 97d2fb
	  char tempfname[] = "XXXXXX";
Packit Service 97d2fb
	  bool use_mkstemp = true;
Packit Service 97d2fb
Packit Service 97d2fb
	  if (oper == oper_print)
Packit Service 97d2fb
	    xfd = STDOUT_FILENO;
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      xfd = mkstemp (tempfname);
Packit Service 97d2fb
	      if (unlikely (xfd == -1))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  /* We cannot create a temporary file.  Try to overwrite
Packit Service 97d2fb
		     the file or create it if it does not exist.  */
Packit Service 97d2fb
		  int flags = O_WRONLY | O_CREAT;
Packit Service 97d2fb
		  if (dont_replace_existing)
Packit Service 97d2fb
		    flags |= O_EXCL;
Packit Service 97d2fb
		  else
Packit Service 97d2fb
		    flags |= O_TRUNC;
Packit Service 97d2fb
		  xfd = open (arhdr->ar_name, flags, 0600);
Packit Service 97d2fb
		  if (unlikely (xfd == -1))
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      int printlen = INT_MAX;
Packit Service 97d2fb
Packit Service 97d2fb
		      if (should_truncate_fname ())
Packit Service 97d2fb
			{
Packit Service 97d2fb
			  /* Try to truncate the name.  First find out by how
Packit Service 97d2fb
			     much.  */
Packit Service 97d2fb
			  printlen = name_max;
Packit Service 97d2fb
			  char truncfname[name_max + 1];
Packit Service 97d2fb
			  *((char *) mempcpy (truncfname, arhdr->ar_name,
Packit Service 97d2fb
					      name_max)) = '\0';
Packit Service 97d2fb
Packit Service 97d2fb
			  xfd = open (truncfname, flags, 0600);
Packit Service 97d2fb
			}
Packit Service 97d2fb
Packit Service 97d2fb
		      if (xfd == -1)
Packit Service 97d2fb
			{
Packit Service 97d2fb
			  error (0, errno, gettext ("cannot open %.*s"),
Packit Service 97d2fb
				 (int) printlen, arhdr->ar_name);
Packit Service 97d2fb
			  status = 1;
Packit Service 97d2fb
			  goto next;
Packit Service 97d2fb
			}
Packit Service 97d2fb
		    }
Packit Service 97d2fb
Packit Service 97d2fb
		  use_mkstemp = false;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  ssize_t n;
Packit Service 97d2fb
	  while ((n = TEMP_FAILURE_RETRY (write (xfd, data, nleft))) != -1)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      nleft -= n;
Packit Service 97d2fb
	      if (nleft == 0)
Packit Service 97d2fb
		break;
Packit Service 97d2fb
	      data += n;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  if (unlikely (n == -1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      error (0, errno, gettext ("failed to write %s"), arhdr->ar_name);
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	      unlink (tempfname);
Packit Service 97d2fb
	      close (xfd);
Packit Service 97d2fb
	      goto next;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  if (oper != oper_print)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* Fix up the mode.  */
Packit Service 97d2fb
	      if (unlikely (fchmod (xfd, arhdr->ar_mode) != 0))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  error (0, errno, gettext ("cannot change mode of %s"),
Packit Service 97d2fb
			 arhdr->ar_name);
Packit Service 97d2fb
		  status = 0;
Packit Service 97d2fb
		}
Packit Service 97d2fb
Packit Service 97d2fb
	      if (preserve_dates)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  struct timespec tv[2];
Packit Service 97d2fb
		  tv[0].tv_sec = arhdr->ar_date;
Packit Service 97d2fb
		  tv[0].tv_nsec = 0;
Packit Service 97d2fb
		  tv[1].tv_sec = arhdr->ar_date;
Packit Service 97d2fb
		  tv[1].tv_nsec = 0;
Packit Service 97d2fb
Packit Service 97d2fb
		  if (unlikely (futimens (xfd, tv) != 0))
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      error (0, errno,
Packit Service 97d2fb
			     gettext ("cannot change modification time of %s"),
Packit Service 97d2fb
			     arhdr->ar_name);
Packit Service 97d2fb
		      status = 1;
Packit Service 97d2fb
		    }
Packit Service 97d2fb
		}
Packit Service 97d2fb
Packit Service 97d2fb
	      /* If we used a temporary file, move it do the right
Packit Service 97d2fb
		 name now.  */
Packit Service 97d2fb
	      if (use_mkstemp)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  int r;
Packit Service 97d2fb
Packit Service 97d2fb
		  if (dont_replace_existing)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      r = link (tempfname, arhdr->ar_name);
Packit Service 97d2fb
		      if (likely (r == 0))
Packit Service 97d2fb
			unlink (tempfname);
Packit Service 97d2fb
		    }
Packit Service 97d2fb
		  else
Packit Service 97d2fb
		    r = rename (tempfname, arhdr->ar_name);
Packit Service 97d2fb
Packit Service 97d2fb
		  if (unlikely (r) != 0)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      int printlen = INT_MAX;
Packit Service 97d2fb
Packit Service 97d2fb
		      if (should_truncate_fname ())
Packit Service 97d2fb
			{
Packit Service 97d2fb
			  /* Try to truncate the name.  First find out by how
Packit Service 97d2fb
			     much.  */
Packit Service 97d2fb
			  printlen = name_max;
Packit Service 97d2fb
			  char truncfname[name_max + 1];
Packit Service 97d2fb
			  *((char *) mempcpy (truncfname, arhdr->ar_name,
Packit Service 97d2fb
					      name_max)) = '\0';
Packit Service 97d2fb
Packit Service 97d2fb
			  if (dont_replace_existing)
Packit Service 97d2fb
			    {
Packit Service 97d2fb
			      r = link (tempfname, truncfname);
Packit Service 97d2fb
			      if (likely (r == 0))
Packit Service 97d2fb
				unlink (tempfname);
Packit Service 97d2fb
			    }
Packit Service 97d2fb
			  else
Packit Service 97d2fb
			    r = rename (tempfname, truncfname);
Packit Service 97d2fb
			}
Packit Service 97d2fb
Packit Service 97d2fb
		      if (r != 0)
Packit Service 97d2fb
			{
Packit Service 97d2fb
			  error (0, errno, gettext ("\
Packit Service 97d2fb
cannot rename temporary file to %.*s"),
Packit Service 97d2fb
				 printlen, arhdr->ar_name);
Packit Service 97d2fb
			  unlink (tempfname);
Packit Service 97d2fb
			  status = 1;
Packit Service 97d2fb
			}
Packit Service 97d2fb
		    }
Packit Service 97d2fb
		}
Packit Service 97d2fb
Packit Service 97d2fb
	      close (xfd);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
    next:
Packit Service 97d2fb
      cmd = elf_next (subelf);
Packit Service 97d2fb
      if (elf_end (subelf) != 0)
Packit Service 97d2fb
	error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  hdestroy ();
Packit Service 97d2fb
Packit Service 97d2fb
  if (force_symtab)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      arlib_finalize ();
Packit Service 97d2fb
Packit Service 97d2fb
      if (symtab.symsnamelen != 0
Packit Service 97d2fb
	  /* We have to rewrite the file also if it initially had an index
Packit Service 97d2fb
	     but now does not need one anymore.  */
Packit Service 97d2fb
	  || (symtab.symsnamelen == 0 && index_size != 0))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  char tmpfname[strlen (arfname) + 7];
Packit Service 97d2fb
	  strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
Packit Service 97d2fb
	  int newfd = mkstemp (tmpfname);
Packit Service 97d2fb
	  if (unlikely (newfd == -1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	    nonew:
Packit Service 97d2fb
	      error (0, errno, gettext ("cannot create new file"));
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* Create the header.  */
Packit Service 97d2fb
	      if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  // XXX Use /prof/self/fd/%d ???
Packit Service 97d2fb
		nonew_unlink:
Packit Service 97d2fb
		  unlink (tmpfname);
Packit Service 97d2fb
		  if (newfd != -1)
Packit Service 97d2fb
		    close (newfd);
Packit Service 97d2fb
		  goto nonew;
Packit Service 97d2fb
		}
Packit Service 97d2fb
Packit Service 97d2fb
	      /* Create the new file.  There are three parts as far we are
Packit Service 97d2fb
		 concerned: 1. original context before the index, 2. the
Packit Service 97d2fb
		 new index, 3. everything after the new index.  */
Packit Service 97d2fb
	      off_t rest_off;
Packit Service 97d2fb
	      if (index_off != -1)
Packit Service 97d2fb
		rest_off = (index_off + sizeof (struct ar_hdr)
Packit Service 97d2fb
			    + ((index_size + 1) & ~1ul));
Packit Service 97d2fb
	      else
Packit Service 97d2fb
		rest_off = SARMAG;
Packit Service 97d2fb
Packit Service 97d2fb
	      if (symtab.symsnamelen != 0
Packit Service 97d2fb
		   && ((write_retry (newfd, symtab.symsoff,
Packit Service 97d2fb
				     symtab.symsofflen)
Packit Service 97d2fb
			!= (ssize_t) symtab.symsofflen)
Packit Service 97d2fb
		       || (write_retry (newfd, symtab.symsname,
Packit Service 97d2fb
					symtab.symsnamelen)
Packit Service 97d2fb
			   != (ssize_t) symtab.symsnamelen)))
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
	      /* Even if the original file had content before the
Packit Service 97d2fb
		 symbol table, we write it in the correct order.  */
Packit Service 97d2fb
	      if ((index_off != SARMAG
Packit Service 97d2fb
		   && copy_content (elf, newfd, SARMAG, index_off - SARMAG))
Packit Service 97d2fb
		  || copy_content (elf, newfd, rest_off, st.st_size - rest_off))
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
	      /* Never complain about fchown failing.  */
Packit Service 97d2fb
	      if (fchown (newfd, st.st_uid, st.st_gid) != 0) { ; }
Packit Service 97d2fb
	      /* Set the mode of the new file to the same values the
Packit Service 97d2fb
		 original file has.  */
Packit Service 97d2fb
	      if (fchmod (newfd, st.st_mode & ALLPERMS) != 0
Packit Service 97d2fb
		  || close (newfd) != 0)
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
	      newfd = -1;
Packit Service 97d2fb
	      if (rename (tmpfname, arfname) != 0)
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  elf_end (elf);
Packit Service 97d2fb
Packit Service 97d2fb
  close (fd);
Packit Service 97d2fb
Packit Service 97d2fb
  not_found (argc, argv, found);
Packit Service 97d2fb
Packit Service 97d2fb
  return status;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
struct armem
Packit Service 97d2fb
{
Packit Service 97d2fb
  off_t off;
Packit Service 97d2fb
  off_t old_off;
Packit Service 97d2fb
  size_t size;
Packit Service 97d2fb
  long int long_name_off;
Packit Service 97d2fb
  struct armem *next;
Packit Service 97d2fb
  void *mem;
Packit Service 97d2fb
  time_t sec;
Packit Service 97d2fb
  uid_t uid;
Packit Service 97d2fb
  gid_t gid;
Packit Service 97d2fb
  mode_t mode;
Packit Service 97d2fb
  const char *name;
Packit Service 97d2fb
  Elf *elf;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
write_member (struct armem *memb, off_t *startp, off_t *lenp, Elf *elf,
Packit Service 97d2fb
	      off_t end_off, int newfd)
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct ar_hdr arhdr;
Packit Service 97d2fb
  /* The ar_name is not actually zero teminated, but we need that for
Packit Service 97d2fb
     snprintf.  Also if the name is too long, then the string starts
Packit Service 97d2fb
     with '/' plus an index off number (decimal).  */
Packit Service 97d2fb
  char tmpbuf[sizeof (arhdr.ar_name) + 2];
Packit Service 97d2fb
Packit Service 97d2fb
  bool changed_header = memb->long_name_off != -1;
Packit Service 97d2fb
  if (changed_header)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* In case of a long file name we assume the archive header
Packit Service 97d2fb
	 changed and we write it here.  */
Packit Service 97d2fb
      memcpy (&arhdr, elf_rawfile (elf, NULL) + *startp, sizeof (arhdr));
Packit Service 97d2fb
Packit Service 97d2fb
      snprintf (tmpbuf, sizeof (tmpbuf), "/%-*ld",
Packit Service 97d2fb
		(int) sizeof (arhdr.ar_name), memb->long_name_off);
Packit Service 97d2fb
      changed_header = memcmp (arhdr.ar_name, tmpbuf,
Packit Service 97d2fb
			       sizeof (arhdr.ar_name)) != 0;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* If the files are adjacent in the old file extend the range.  */
Packit Service 97d2fb
  if (*startp != -1 && !changed_header && *startp + *lenp == memb->old_off)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Extend the current range.  */
Packit Service 97d2fb
      *lenp += (memb->next != NULL
Packit Service 97d2fb
		? memb->next->off : end_off) - memb->off;
Packit Service 97d2fb
      return 0;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Write out the old range.  */
Packit Service 97d2fb
  if (*startp != -1 && copy_content (elf, newfd, *startp, *lenp))
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  *startp = memb->old_off;
Packit Service 97d2fb
  *lenp = (memb->next != NULL ? memb->next->off : end_off) - memb->off;
Packit Service 97d2fb
Packit Service 97d2fb
  if (changed_header)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
Packit Service 97d2fb
Packit Service 97d2fb
      if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
Packit Service 97d2fb
		    != sizeof (arhdr)))
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
Packit Service 97d2fb
      *startp += sizeof (struct ar_hdr);
Packit Service 97d2fb
      assert ((size_t) *lenp >= sizeof (struct ar_hdr));
Packit Service 97d2fb
      *lenp -= sizeof (struct ar_hdr);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Store the name in the long name table if necessary.
Packit Service 97d2fb
   Record its offset or -1 if we did not need to use the table.  */
Packit Service 97d2fb
static void
Packit Service 97d2fb
remember_long_name (struct armem *mem, const char *name, size_t namelen)
Packit Service 97d2fb
{
Packit Service 97d2fb
  mem->long_name_off = (namelen > MAX_AR_NAME_LEN
Packit Service 97d2fb
			? arlib_add_long_name (name, namelen)
Packit Service 97d2fb
			: -1l);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
do_oper_delete (const char *arfname, char **argv, int argc,
Packit Service 97d2fb
		long int instance)
Packit Service 97d2fb
{
Packit Service 97d2fb
  bool *found = alloca (sizeof (bool) * argc);
Packit Service 97d2fb
  memset (found, '\0', sizeof (bool) * argc);
Packit Service 97d2fb
Packit Service 97d2fb
  /* List of the files we keep.  */
Packit Service 97d2fb
  struct armem *to_copy = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  int status = 0;
Packit Service 97d2fb
  Elf *elf;
Packit Service 97d2fb
  struct stat st;
Packit Service 97d2fb
  int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, false);
Packit Service 97d2fb
Packit Service 97d2fb
  if (hcreate (2 * argc) == 0)
Packit Service 97d2fb
    error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
Packit Service 97d2fb
Packit Service 97d2fb
  for (int cnt = 0; cnt < argc; ++cnt)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
Packit Service 97d2fb
      if (hsearch (entry, ENTER) == NULL)
Packit Service 97d2fb
	error (EXIT_FAILURE, errno,
Packit Service 97d2fb
	       gettext ("cannot insert into hash table"));
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  arlib_init ();
Packit Service 97d2fb
Packit Service 97d2fb
  off_t cur_off = SARMAG;
Packit Service 97d2fb
  Elf_Cmd cmd = ELF_C_READ_MMAP;
Packit Service 97d2fb
  Elf *subelf;
Packit Service 97d2fb
  while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Elf_Arhdr *arhdr = elf_getarhdr (subelf);
Packit Service 97d2fb
Packit Service 97d2fb
      /* Ignore the symbol table and the long file name table here.  */
Packit Service 97d2fb
      if (strcmp (arhdr->ar_name, "/") == 0
Packit Service 97d2fb
	  || strcmp (arhdr->ar_name, "//") == 0)
Packit Service 97d2fb
	goto next;
Packit Service 97d2fb
Packit Service 97d2fb
      bool do_delete = argc <= 0;
Packit Service 97d2fb
      if (!do_delete)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  ENTRY entry;
Packit Service 97d2fb
	  entry.key = arhdr->ar_name;
Packit Service 97d2fb
	  ENTRY *res = hsearch (entry, FIND);
Packit Service 97d2fb
	  if (res != NULL && (instance < 0 || instance-- == 0)
Packit Service 97d2fb
	      && !found[(char **) res->data - argv])
Packit Service 97d2fb
	    found[(char **) res->data - argv] = do_delete = true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (do_delete)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (verbose)
Packit Service 97d2fb
	    printf ("d - %s\n", arhdr->ar_name);
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  struct armem *newp = alloca (sizeof (struct armem));
Packit Service 97d2fb
	  newp->old_off = elf_getaroff (subelf);
Packit Service 97d2fb
	  newp->off = cur_off;
Packit Service 97d2fb
Packit Service 97d2fb
	  cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
Packit Service 97d2fb
		      + sizeof (struct ar_hdr));
Packit Service 97d2fb
Packit Service 97d2fb
	  if (to_copy == NULL)
Packit Service 97d2fb
	    to_copy = newp->next = newp;
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      newp->next = to_copy->next;
Packit Service 97d2fb
	      to_copy = to_copy->next = newp;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  /* If we recreate the symbol table read the file's symbol
Packit Service 97d2fb
	     table now.  */
Packit Service 97d2fb
	  arlib_add_symbols (subelf, arfname, arhdr->ar_name, newp->off);
Packit Service 97d2fb
Packit Service 97d2fb
	  /* Remember long file names.  */
Packit Service 97d2fb
	  remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
    next:
Packit Service 97d2fb
      cmd = elf_next (subelf);
Packit Service 97d2fb
      if (elf_end (subelf) != 0)
Packit Service 97d2fb
	error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  arlib_finalize ();
Packit Service 97d2fb
Packit Service 97d2fb
  hdestroy ();
Packit Service 97d2fb
Packit Service 97d2fb
  /* Create a new, temporary file in the same directory as the
Packit Service 97d2fb
     original file.  */
Packit Service 97d2fb
  char tmpfname[strlen (arfname) + 7];
Packit Service 97d2fb
  strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
Packit Service 97d2fb
  int newfd = mkstemp (tmpfname);
Packit Service 97d2fb
  if (unlikely (newfd == -1))
Packit Service 97d2fb
    goto nonew;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Create the header.  */
Packit Service 97d2fb
  if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      // XXX Use /prof/self/fd/%d ???
Packit Service 97d2fb
    nonew_unlink:
Packit Service 97d2fb
      unlink (tmpfname);
Packit Service 97d2fb
      if (newfd != -1)
Packit Service 97d2fb
	close (newfd);
Packit Service 97d2fb
    nonew:
Packit Service 97d2fb
      error (0, errno, gettext ("cannot create new file"));
Packit Service 97d2fb
      status = 1;
Packit Service 97d2fb
      goto errout;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* If the archive is empty that is all we have to do.  */
Packit Service 97d2fb
  if (likely (to_copy != NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Write the symbol table or the long file name table or both.  */
Packit Service 97d2fb
      if (symtab.symsnamelen != 0
Packit Service 97d2fb
	  && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
Packit Service 97d2fb
	       != (ssize_t) symtab.symsofflen)
Packit Service 97d2fb
	      || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
Packit Service 97d2fb
		  != (ssize_t) symtab.symsnamelen)))
Packit Service 97d2fb
	goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
      if (symtab.longnameslen > sizeof (struct ar_hdr)
Packit Service 97d2fb
	  && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
Packit Service 97d2fb
	      != (ssize_t) symtab.longnameslen))
Packit Service 97d2fb
	goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
      /* NULL-terminate the list of files to copy.  */
Packit Service 97d2fb
      struct armem *last = to_copy;
Packit Service 97d2fb
      to_copy = to_copy->next;
Packit Service 97d2fb
      last->next = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
      off_t start = -1;
Packit Service 97d2fb
      off_t len = -1;
Packit Service 97d2fb
Packit Service 97d2fb
      do
Packit Service 97d2fb
	if (write_member (to_copy, &start, &len, elf, cur_off, newfd) != 0)
Packit Service 97d2fb
	  goto nonew_unlink;
Packit Service 97d2fb
      while ((to_copy = to_copy->next) != NULL);
Packit Service 97d2fb
Packit Service 97d2fb
      /* Write the last part.  */
Packit Service 97d2fb
      if (copy_content (elf, newfd, start, len))
Packit Service 97d2fb
	goto nonew_unlink;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Set the mode of the new file to the same values the original file
Packit Service 97d2fb
     has.  Never complain about fchown failing.  But do it before
Packit Service 97d2fb
     setting the mode (which might be reset/ignored if the owner is
Packit Service 97d2fb
     wrong.  */
Packit Service 97d2fb
  if (fchown (newfd, st.st_uid, st.st_gid) != 0) { ; }
Packit Service 97d2fb
  if (fchmod (newfd, st.st_mode & ALLPERMS) != 0
Packit Service 97d2fb
      || close (newfd) != 0)
Packit Service 97d2fb
    goto nonew_unlink;
Packit Service 97d2fb
  newfd = -1;
Packit Service 97d2fb
  if (rename (tmpfname, arfname) != 0)
Packit Service 97d2fb
    goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
 errout:
Packit Service 97d2fb
  elf_end (elf);
Packit Service 97d2fb
Packit Service 97d2fb
  arlib_fini ();
Packit Service 97d2fb
Packit Service 97d2fb
  close (fd);
Packit Service 97d2fb
Packit Service 97d2fb
  not_found (argc, argv, found);
Packit Service 97d2fb
Packit Service 97d2fb
  return status;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Prints the given value in the given buffer without a trailing zero char.
Packit Service 97d2fb
   Returns false if the given value doesn't fit in the given buffer.  */
Packit Service 97d2fb
static bool
Packit Service 97d2fb
no0print (bool ofmt, char *buf, int bufsize, long int val)
Packit Service 97d2fb
{
Packit Service 97d2fb
  char tmpbuf[bufsize + 1];
Packit Service 97d2fb
  int ret = snprintf (tmpbuf, sizeof (tmpbuf), ofmt ? "%-*lo" : "%-*ld",
Packit Service 97d2fb
		      bufsize, val);
Packit Service 97d2fb
  if (ret >= (int) sizeof (tmpbuf))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  memcpy (buf, tmpbuf, bufsize);
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
do_oper_insert (int oper, const char *arfname, char **argv, int argc,
Packit Service 97d2fb
		const char *member)
Packit Service 97d2fb
{
Packit Service 97d2fb
  int status = 0;
Packit Service 97d2fb
  Elf *elf = NULL;
Packit Service 97d2fb
  struct stat st;
Packit Service 97d2fb
  int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, oper != oper_move);
Packit Service 97d2fb
Packit Service 97d2fb
  /* List of the files we keep.  */
Packit Service 97d2fb
  struct armem *all = NULL;
Packit Service 97d2fb
  struct armem *after_memberelem = NULL;
Packit Service 97d2fb
  struct armem **found = alloca (sizeof (*found) * argc);
Packit Service 97d2fb
  memset (found, '\0', sizeof (*found) * argc);
Packit Service 97d2fb
Packit Service 97d2fb
  arlib_init ();
Packit Service 97d2fb
Packit Service 97d2fb
  /* Initialize early for no_old case.  */
Packit Service 97d2fb
  off_t cur_off = SARMAG;
Packit Service 97d2fb
Packit Service 97d2fb
  if (fd == -1)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (!suppress_create_msg)
Packit Service 97d2fb
	fprintf (stderr, "%s: creating %s\n",
Packit Service 97d2fb
		 program_invocation_short_name, arfname);
Packit Service 97d2fb
Packit Service 97d2fb
      goto no_old;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Store the names of all files from the command line in a hash
Packit Service 97d2fb
     table so that we can match it.  Note that when no file name is
Packit Service 97d2fb
     given we are basically doing nothing except recreating the
Packit Service 97d2fb
     index.  */
Packit Service 97d2fb
  if (oper != oper_qappend)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (hcreate (2 * argc) == 0)
Packit Service 97d2fb
	error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
Packit Service 97d2fb
Packit Service 97d2fb
      for (int cnt = 0; cnt < argc; ++cnt)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  ENTRY entry;
Packit Service 97d2fb
	  entry.key = full_path ? argv[cnt] : basename (argv[cnt]);
Packit Service 97d2fb
	  entry.data = &argv[cnt];
Packit Service 97d2fb
	  if (hsearch (entry, ENTER) == NULL)
Packit Service 97d2fb
	    error (EXIT_FAILURE, errno,
Packit Service 97d2fb
		   gettext ("cannot insert into hash table"));
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* While iterating over the current content of the archive we must
Packit Service 97d2fb
     determine a number of things: which archive members to keep,
Packit Service 97d2fb
     which are replaced, and where to insert the new members.  */
Packit Service 97d2fb
  Elf_Cmd cmd = ELF_C_READ_MMAP;
Packit Service 97d2fb
  Elf *subelf;
Packit Service 97d2fb
  while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Elf_Arhdr *arhdr = elf_getarhdr (subelf);
Packit Service 97d2fb
Packit Service 97d2fb
      /* Ignore the symbol table and the long file name table here.  */
Packit Service 97d2fb
      if (strcmp (arhdr->ar_name, "/") == 0
Packit Service 97d2fb
	  || strcmp (arhdr->ar_name, "//") == 0)
Packit Service 97d2fb
	goto next;
Packit Service 97d2fb
Packit Service 97d2fb
      struct armem *newp = alloca (sizeof (struct armem));
Packit Service 97d2fb
      newp->old_off = elf_getaroff (subelf);
Packit Service 97d2fb
      newp->size = arhdr->ar_size;
Packit Service 97d2fb
      newp->sec = arhdr->ar_date;
Packit Service 97d2fb
      newp->mem = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Remember long file names.  */
Packit Service 97d2fb
      remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
Packit Service 97d2fb
Packit Service 97d2fb
      /* Check whether this is a file we are looking for.  */
Packit Service 97d2fb
      if (oper != oper_qappend)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Check whether this is the member used as the insert point.  */
Packit Service 97d2fb
	  if (member != NULL && strcmp (arhdr->ar_name, member) == 0)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* Note that all == NULL means insert at the beginning.  */
Packit Service 97d2fb
	      if (ipos == ipos_before)
Packit Service 97d2fb
		after_memberelem = all;
Packit Service 97d2fb
	      else
Packit Service 97d2fb
		after_memberelem = newp;
Packit Service 97d2fb
	      member = NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  ENTRY entry;
Packit Service 97d2fb
	  entry.key = arhdr->ar_name;
Packit Service 97d2fb
	  ENTRY *res = hsearch (entry, FIND);
Packit Service 97d2fb
	  if (res != NULL && found[(char **) res->data - argv] == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      found[(char **) res->data - argv] = newp;
Packit Service 97d2fb
Packit Service 97d2fb
	      /* If we insert before or after a certain element move
Packit Service 97d2fb
		 all files to a special list.  */
Packit Service 97d2fb
	      if (unlikely (ipos != ipos_none || oper == oper_move))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  if (after_memberelem == newp)
Packit Service 97d2fb
		    /* Since we remove this element even though we should
Packit Service 97d2fb
		       insert everything after it, we in fact insert
Packit Service 97d2fb
		       everything after the previous element.  */
Packit Service 97d2fb
		    after_memberelem = all;
Packit Service 97d2fb
Packit Service 97d2fb
		  goto next;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (all == NULL)
Packit Service 97d2fb
	all = newp->next = newp;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  newp->next = all->next;
Packit Service 97d2fb
	  all = all->next = newp;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
    next:
Packit Service 97d2fb
      cmd = elf_next (subelf);
Packit Service 97d2fb
      if (elf_end (subelf) != 0)
Packit Service 97d2fb
	error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (oper != oper_qappend)
Packit Service 97d2fb
    hdestroy ();
Packit Service 97d2fb
Packit Service 97d2fb
 no_old:
Packit Service 97d2fb
  if (member != NULL)
Packit Service 97d2fb
    error (EXIT_FAILURE, 0, gettext ("position member %s not found"),
Packit Service 97d2fb
	   member);
Packit Service 97d2fb
Packit Service 97d2fb
  if (oper == oper_move)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Make sure all requested elements are found in the archive.  */
Packit Service 97d2fb
      for (int cnt = 0; cnt < argc; ++cnt)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (found[cnt] == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      fprintf (stderr, gettext ("%s: no entry %s in archive!\n"),
Packit Service 97d2fb
		       program_invocation_short_name, argv[cnt]);
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  if (verbose)
Packit Service 97d2fb
	    printf ("m - %s\n", argv[cnt]);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Open all the new files, get their sizes and add all symbols.  */
Packit Service 97d2fb
      for (int cnt = 0; cnt < argc; ++cnt)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  const char *bname = basename (argv[cnt]);
Packit Service 97d2fb
	  size_t bnamelen = strlen (bname);
Packit Service 97d2fb
	  if (found[cnt] == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      found[cnt] = alloca (sizeof (struct armem));
Packit Service 97d2fb
	      found[cnt]->old_off = -1;
Packit Service 97d2fb
Packit Service 97d2fb
	      remember_long_name (found[cnt], bname, bnamelen);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  struct stat newst;
Packit Service 97d2fb
	  Elf *newelf;
Packit Service 97d2fb
	  int newfd = open (argv[cnt], O_RDONLY);
Packit Service 97d2fb
	  if (newfd == -1)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      error (0, errno, gettext ("cannot open %s"), argv[cnt]);
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else if (fstat (newfd, &newst) == -1)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      error (0, errno, gettext ("cannot stat %s"), argv[cnt]);
Packit Service 97d2fb
	      close (newfd);
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else if (!S_ISREG (newst.st_mode))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      error (0, errno, gettext ("%s is no regular file"), argv[cnt]);
Packit Service 97d2fb
	      close (newfd);
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else if (update_newer
Packit Service 97d2fb
		   && found[cnt]->old_off != -1l
Packit Service 97d2fb
		   && found[cnt]->sec > st.st_mtime)
Packit Service 97d2fb
	    /* Do nothing, the file in the archive is younger.  */
Packit Service 97d2fb
	    close (newfd);
Packit Service 97d2fb
	  else if ((newelf = elf_begin (newfd, ELF_C_READ_MMAP, NULL))
Packit Service 97d2fb
		   == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      fprintf (stderr,
Packit Service 97d2fb
		       gettext ("cannot get ELF descriptor for %s: %s\n"),
Packit Service 97d2fb
		       argv[cnt], elf_errmsg (-1));
Packit Service 97d2fb
	      status = 1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      if (verbose)
Packit Service 97d2fb
		printf ("%c - %s\n",
Packit Service 97d2fb
			found[cnt]->old_off == -1l ? 'a' : 'r', argv[cnt]);
Packit Service 97d2fb
Packit Service 97d2fb
	      found[cnt]->elf = newelf;
Packit Service 97d2fb
	      found[cnt]->sec = arlib_deterministic_output ? 0 : newst.st_mtime;
Packit Service 97d2fb
	      found[cnt]->uid = arlib_deterministic_output ? 0 : newst.st_uid;
Packit Service 97d2fb
	      found[cnt]->gid = arlib_deterministic_output ? 0 : newst.st_gid;
Packit Service 97d2fb
	      found[cnt]->mode = newst.st_mode;
Packit Service 97d2fb
	      found[cnt]->name = bname;
Packit Service 97d2fb
Packit Service 97d2fb
	      found[cnt]->mem = elf_rawfile (newelf, &found[cnt]->size);
Packit Service 97d2fb
	      if (found[cnt]->mem == NULL
Packit Service 97d2fb
		  || elf_cntl (newelf, ELF_C_FDDONE) != 0)
Packit Service 97d2fb
		error (EXIT_FAILURE, 0, gettext ("cannot read %s: %s"),
Packit Service 97d2fb
		       argv[cnt], elf_errmsg (-1));
Packit Service 97d2fb
Packit Service 97d2fb
	      close (newfd);
Packit Service 97d2fb
Packit Service 97d2fb
	      if (found[cnt]->old_off != -1l)
Packit Service 97d2fb
		/* Remember long file names.  */
Packit Service 97d2fb
		remember_long_name (found[cnt], bname, bnamelen);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (status != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      elf_end (elf);
Packit Service 97d2fb
Packit Service 97d2fb
      arlib_fini ();
Packit Service 97d2fb
Packit Service 97d2fb
      close (fd);
Packit Service 97d2fb
Packit Service 97d2fb
      return status;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* If we have no entry point so far add at the end.  AFTER_MEMBERELEM
Packit Service 97d2fb
     being NULL when adding before an entry means add at the beginning.  */
Packit Service 97d2fb
  if (ipos != ipos_before && after_memberelem == NULL)
Packit Service 97d2fb
    after_memberelem = all;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Convert the circular list into a normal list first.  */
Packit Service 97d2fb
  if (all != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      struct armem *tmp = all;
Packit Service 97d2fb
      all = all->next;
Packit Service 97d2fb
      tmp->next = NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  struct armem *last_added = after_memberelem;
Packit Service 97d2fb
  for (int cnt = 0; cnt < argc; ++cnt)
Packit Service 97d2fb
    if (oper != oper_replace || found[cnt]->old_off == -1)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	if (last_added == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    found[cnt]->next = all;
Packit Service 97d2fb
	    last_added = all = found[cnt];
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    found[cnt]->next = last_added->next;
Packit Service 97d2fb
	    last_added = last_added->next = found[cnt];
Packit Service 97d2fb
	  }
Packit Service 97d2fb
      }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Finally compute the offset and add the symbols for the files
Packit Service 97d2fb
     after the insert point.  */
Packit Service 97d2fb
  if (likely (all != NULL))
Packit Service 97d2fb
    for (struct armem *memp = all; memp != NULL; memp = memp->next)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	memp->off = cur_off;
Packit Service 97d2fb
Packit Service 97d2fb
	if (memp->mem == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    Elf_Arhdr *arhdr;
Packit Service 97d2fb
	    /* Fake initializing arhdr and subelf to keep gcc calm.  */
Packit Service 97d2fb
	    asm ("" : "=m" (arhdr), "=m" (subelf));
Packit Service 97d2fb
	    if (elf_rand (elf, memp->old_off) == 0
Packit Service 97d2fb
		|| (subelf = elf_begin (fd, ELF_C_READ_MMAP, elf)) == NULL
Packit Service 97d2fb
		|| (arhdr = elf_getarhdr (subelf)) == NULL)
Packit Service 97d2fb
	      /* This should never happen since we already looked at the
Packit Service 97d2fb
		 archive content.  But who knows...  */
Packit Service 97d2fb
	      error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
Packit Service 97d2fb
Packit Service 97d2fb
	    arlib_add_symbols (subelf, arfname, arhdr->ar_name, cur_off);
Packit Service 97d2fb
Packit Service 97d2fb
	    elf_end (subelf);
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else
Packit Service 97d2fb
	  arlib_add_symbols (memp->elf, arfname, memp->name, cur_off);
Packit Service 97d2fb
Packit Service 97d2fb
	cur_off += (((memp->size + 1) & ~((off_t) 1))
Packit Service 97d2fb
		    + sizeof (struct ar_hdr));
Packit Service 97d2fb
      }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Now we have all the information for the symbol table and long
Packit Service 97d2fb
     file name table.  Construct the final layout.  */
Packit Service 97d2fb
  arlib_finalize ();
Packit Service 97d2fb
Packit Service 97d2fb
  /* Create a new, temporary file in the same directory as the
Packit Service 97d2fb
     original file.  */
Packit Service 97d2fb
  char tmpfname[strlen (arfname) + 7];
Packit Service 97d2fb
  strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
Packit Service 97d2fb
  int newfd;
Packit Service 97d2fb
  if (fd != -1)
Packit Service 97d2fb
    newfd = mkstemp (tmpfname);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      newfd = open (arfname, O_RDWR | O_CREAT | O_EXCL, DEFFILEMODE);
Packit Service 97d2fb
      if (newfd == -1 && errno == EEXIST)
Packit Service 97d2fb
	/* Bah, first the file did not exist, now it does.  Restart.  */
Packit Service 97d2fb
	return do_oper_insert (oper, arfname, argv, argc, member);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (unlikely (newfd == -1))
Packit Service 97d2fb
    goto nonew;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Create the header.  */
Packit Service 97d2fb
  if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
Packit Service 97d2fb
    {
Packit Service 97d2fb
    nonew_unlink:
Packit Service 97d2fb
      if (fd != -1)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  // XXX Use /prof/self/fd/%d ???
Packit Service 97d2fb
	  unlink (tmpfname);
Packit Service 97d2fb
	  if (newfd != -1)
Packit Service 97d2fb
	    close (newfd);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    nonew:
Packit Service 97d2fb
      error (0, errno, gettext ("cannot create new file"));
Packit Service 97d2fb
      status = 1;
Packit Service 97d2fb
      goto errout;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* If the new archive is not empty we actually have something to do.  */
Packit Service 97d2fb
  if (likely (all != NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Write the symbol table or the long file name table or both.  */
Packit Service 97d2fb
      if (symtab.symsnamelen != 0
Packit Service 97d2fb
	  && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
Packit Service 97d2fb
	       != (ssize_t) symtab.symsofflen)
Packit Service 97d2fb
	      || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
Packit Service 97d2fb
		  != (ssize_t) symtab.symsnamelen)))
Packit Service 97d2fb
	goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
      if (symtab.longnameslen > sizeof (struct ar_hdr)
Packit Service 97d2fb
	  && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
Packit Service 97d2fb
	      != (ssize_t) symtab.longnameslen))
Packit Service 97d2fb
	goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
      off_t start = -1;
Packit Service 97d2fb
      off_t len = -1;
Packit Service 97d2fb
Packit Service 97d2fb
      while (all != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (all->mem != NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* This is a new file.  If there is anything from the
Packit Service 97d2fb
		 archive left to be written do it now.  */
Packit Service 97d2fb
	      if (start != -1  && copy_content (elf, newfd, start, len))
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
	      start = -1;
Packit Service 97d2fb
	      len = -1;
Packit Service 97d2fb
Packit Service 97d2fb
	      /* Create the header.  */
Packit Service 97d2fb
	      struct ar_hdr arhdr;
Packit Service 97d2fb
	      /* The ar_name is not actually zero teminated, but we
Packit Service 97d2fb
		 need that for snprintf.  Also if the name is too
Packit Service 97d2fb
		 long, then the string starts with '/' plus an index
Packit Service 97d2fb
		 off number (decimal).  */
Packit Service 97d2fb
	      char tmpbuf[sizeof (arhdr.ar_name) + 2];
Packit Service 97d2fb
	      if (all->long_name_off == -1)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  size_t namelen = strlen (all->name);
Packit Service 97d2fb
		  char *p = mempcpy (arhdr.ar_name, all->name, namelen);
Packit Service 97d2fb
		  *p++ = '/';
Packit Service 97d2fb
		  memset (p, ' ', sizeof (arhdr.ar_name) - namelen - 1);
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      else
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  snprintf (tmpbuf, sizeof (tmpbuf), "/%-*ld",
Packit Service 97d2fb
			    (int) sizeof (arhdr.ar_name), all->long_name_off);
Packit Service 97d2fb
		  memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
Packit Service 97d2fb
		}
Packit Service 97d2fb
Packit Service 97d2fb
	      if (! no0print (false, arhdr.ar_date, sizeof (arhdr.ar_date),
Packit Service 97d2fb
			      all->sec))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  error (0, errno, gettext ("cannot represent ar_date"));
Packit Service 97d2fb
		  goto nonew_unlink;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      if (! no0print (false, arhdr.ar_uid, sizeof (arhdr.ar_uid),
Packit Service 97d2fb
			      all->uid))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  error (0, errno, gettext ("cannot represent ar_uid"));
Packit Service 97d2fb
		  goto nonew_unlink;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      if (! no0print (false, arhdr.ar_gid, sizeof (arhdr.ar_gid),
Packit Service 97d2fb
			      all->gid))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  error (0, errno, gettext ("cannot represent ar_gid"));
Packit Service 97d2fb
		  goto nonew_unlink;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      if (! no0print (true, arhdr.ar_mode, sizeof (arhdr.ar_mode),
Packit Service 97d2fb
			all->mode))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  error (0, errno, gettext ("cannot represent ar_mode"));
Packit Service 97d2fb
		  goto nonew_unlink;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      if (! no0print (false, arhdr.ar_size, sizeof (arhdr.ar_size),
Packit Service 97d2fb
			all->size))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  error (0, errno, gettext ("cannot represent ar_size"));
Packit Service 97d2fb
		  goto nonew_unlink;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      memcpy (arhdr.ar_fmag, ARFMAG, sizeof (arhdr.ar_fmag));
Packit Service 97d2fb
Packit Service 97d2fb
	      if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
Packit Service 97d2fb
			    != sizeof (arhdr)))
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
	      /* Now the file itself.  */
Packit Service 97d2fb
	      if (unlikely (write_retry (newfd, all->mem, all->size)
Packit Service 97d2fb
			    != (off_t) all->size))
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
Packit Service 97d2fb
	      /* Pad the file if its size is odd.  */
Packit Service 97d2fb
	      if ((all->size & 1) != 0)
Packit Service 97d2fb
		if (unlikely (write_retry (newfd, "\n", 1) != 1))
Packit Service 97d2fb
		  goto nonew_unlink;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* This is a member from the archive.  */
Packit Service 97d2fb
	      if (write_member (all, &start, &len, elf, cur_off, newfd)
Packit Service 97d2fb
		  != 0)
Packit Service 97d2fb
		goto nonew_unlink;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  all = all->next;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* Write the last part.  */
Packit Service 97d2fb
      if (start != -1 && copy_content (elf, newfd, start, len))
Packit Service 97d2fb
	goto nonew_unlink;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Set the mode of the new file to the same values the original file
Packit Service 97d2fb
     has.  */
Packit Service 97d2fb
  if (fd != -1)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Never complain about fchown failing.  But do it before
Packit Service 97d2fb
	 setting the modes, or they might be reset/ignored if the
Packit Service 97d2fb
	 owner is wrong.  */
Packit Service 97d2fb
      if (fchown (newfd, st.st_uid, st.st_gid) != 0) { ; }
Packit Service 97d2fb
      if (fchmod (newfd, st.st_mode & ALLPERMS) != 0
Packit Service 97d2fb
	  || close (newfd) != 0)
Packit Service 97d2fb
        goto nonew_unlink;
Packit Service 97d2fb
      newfd = -1;
Packit Service 97d2fb
      if (rename (tmpfname, arfname) != 0)
Packit Service 97d2fb
	goto nonew_unlink;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
 errout:
Packit Service 97d2fb
  for (int cnt = 0; cnt < argc; ++cnt)
Packit Service 97d2fb
    elf_end (found[cnt]->elf);
Packit Service 97d2fb
Packit Service 97d2fb
  elf_end (elf);
Packit Service 97d2fb
Packit Service 97d2fb
  arlib_fini ();
Packit Service 97d2fb
Packit Service 97d2fb
  if (fd != -1)
Packit Service 97d2fb
    close (fd);
Packit Service 97d2fb
Packit Service 97d2fb
  return status;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
#include "debugpred.h"