mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-06 12:09:26 +08:00
014cc7f849
smart_rename is capable of handling symlinks by copying and it also tries to preserve ownership and permissions of files when they're overwritten during the rename. This is useful in objcopy where the file properties need to be preserved. However because smart_rename does this using file names, it leaves a race window between renames and permission fixes. This change removes this race window by using file descriptors from the original BFDs that were used to manipulate these files wherever possible. The file that is to be renamed is also passed as a file descriptor so that we use fchown/fchmod on the file descriptor, thus making sure that we only modify the file we have opened to write. Further, in case the file is to be overwritten (as is the case in ar or objcopy), the permissions that need to be restored are taken from the file descriptor that was opened for input so that integrity of the file status is maintained all the way through to the rename. binutils/ * rename.c * ar.c (write_archive) [!defined (_WIN32) || defined (__CYGWIN32__)]: Initialize TARGET_STAT and OFD to pass to SMART_RENAME. * arsup.c (ar_save) [defined (_WIN32) || defined (__CYGWIN32__)]: Likewise. * bucomm.h (smart_rename): Add new arguments to declaration. * objcopy.c (strip_main)[defined (_WIN32) || defined (__CYGWIN32__)]: Initialize COPYFD and pass to SMART_RENAME. (copy_main) [defined (_WIN32) || defined (__CYGWIN32__)]: Likewise. * rename.c (try_preserve_permissions): New function. (smart_rename): Use it and add new arguments.
509 lines
10 KiB
C
509 lines
10 KiB
C
/* arsup.c - Archive support for MRI compatibility
|
|
Copyright (C) 1992-2020 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
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 3 of the License, 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., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
|
|
/* Contributed by Steve Chamberlain
|
|
sac@cygnus.com
|
|
|
|
This file looks after requests from arparse.y, to provide the MRI
|
|
style librarian command syntax + 1 word LIST. */
|
|
|
|
#include "sysdep.h"
|
|
#include "bfd.h"
|
|
#include "libiberty.h"
|
|
#include "filenames.h"
|
|
#include "bucomm.h"
|
|
#include "arsup.h"
|
|
|
|
static void map_over_list
|
|
(bfd *, void (*function) (bfd *, bfd *), struct list *);
|
|
static void ar_directory_doer (bfd *, bfd *);
|
|
static void ar_addlib_doer (bfd *, bfd *);
|
|
|
|
extern int verbose;
|
|
extern int deterministic;
|
|
|
|
static bfd *obfd;
|
|
static char *real_name;
|
|
static FILE *outfile;
|
|
|
|
static void
|
|
map_over_list (bfd *arch, void (*function) (bfd *, bfd *), struct list *list)
|
|
{
|
|
bfd *head;
|
|
|
|
if (list == NULL)
|
|
{
|
|
bfd *next;
|
|
|
|
head = arch->archive_next;
|
|
while (head != NULL)
|
|
{
|
|
next = head->archive_next;
|
|
function (head, (bfd *) NULL);
|
|
head = next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct list *ptr;
|
|
|
|
/* This may appear to be a baroque way of accomplishing what we
|
|
want. however we have to iterate over the filenames in order
|
|
to notice where a filename is requested but does not exist in
|
|
the archive. Ditto mapping over each file each time -- we
|
|
want to hack multiple references. */
|
|
for (ptr = list; ptr; ptr = ptr->next)
|
|
{
|
|
bfd_boolean found = FALSE;
|
|
bfd *prev = arch;
|
|
|
|
for (head = arch->archive_next; head; head = head->archive_next)
|
|
{
|
|
if (bfd_get_filename (head) != NULL
|
|
&& FILENAME_CMP (ptr->name, bfd_get_filename (head)) == 0)
|
|
{
|
|
found = TRUE;
|
|
function (head, prev);
|
|
}
|
|
prev = head;
|
|
}
|
|
if (! found)
|
|
fprintf (stderr, _("No entry %s in archive.\n"), ptr->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
ar_directory_doer (bfd *abfd, bfd *ignore ATTRIBUTE_UNUSED)
|
|
{
|
|
print_arelt_descr(outfile, abfd, verbose, FALSE);
|
|
}
|
|
|
|
void
|
|
ar_directory (char *ar_name, struct list *list, char *output)
|
|
{
|
|
bfd *arch;
|
|
|
|
arch = open_inarch (ar_name, (char *) NULL);
|
|
if (output)
|
|
{
|
|
outfile = fopen(output,"w");
|
|
if (outfile == 0)
|
|
{
|
|
outfile = stdout;
|
|
fprintf (stderr,_("Can't open file %s\n"), output);
|
|
output = 0;
|
|
}
|
|
}
|
|
else
|
|
outfile = stdout;
|
|
|
|
map_over_list (arch, ar_directory_doer, list);
|
|
|
|
bfd_close (arch);
|
|
|
|
if (output)
|
|
fclose (outfile);
|
|
}
|
|
|
|
void
|
|
prompt (void)
|
|
{
|
|
extern int interactive;
|
|
|
|
if (interactive)
|
|
{
|
|
printf ("AR >");
|
|
fflush (stdout);
|
|
}
|
|
}
|
|
|
|
void
|
|
maybequit (void)
|
|
{
|
|
if (! interactive)
|
|
xexit (9);
|
|
}
|
|
|
|
|
|
void
|
|
ar_open (char *name, int t)
|
|
{
|
|
char *tname;
|
|
const char *bname = lbasename (name);
|
|
real_name = name;
|
|
|
|
/* Prepend tmp- to the beginning, to avoid file-name clashes after
|
|
truncation on filesystems with limited namespaces (DOS). */
|
|
if (asprintf (&tname, "%.*stmp-%s", (int) (bname - name), name, bname) == -1)
|
|
{
|
|
fprintf (stderr, _("%s: Can't allocate memory for temp name (%s)\n"),
|
|
program_name, strerror(errno));
|
|
maybequit ();
|
|
return;
|
|
}
|
|
|
|
obfd = bfd_openw (tname, NULL);
|
|
|
|
if (!obfd)
|
|
{
|
|
fprintf (stderr,
|
|
_("%s: Can't open output archive %s\n"),
|
|
program_name, tname);
|
|
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
if (!t)
|
|
{
|
|
bfd **ptr;
|
|
bfd *element;
|
|
bfd *ibfd;
|
|
|
|
ibfd = bfd_openr (name, NULL);
|
|
|
|
if (!ibfd)
|
|
{
|
|
fprintf (stderr,_("%s: Can't open input archive %s\n"),
|
|
program_name, name);
|
|
maybequit ();
|
|
return;
|
|
}
|
|
|
|
if (!bfd_check_format(ibfd, bfd_archive))
|
|
{
|
|
fprintf (stderr,
|
|
_("%s: file %s is not an archive\n"),
|
|
program_name, name);
|
|
maybequit ();
|
|
return;
|
|
}
|
|
|
|
ptr = &(obfd->archive_head);
|
|
element = bfd_openr_next_archived_file (ibfd, NULL);
|
|
|
|
while (element)
|
|
{
|
|
*ptr = element;
|
|
ptr = &element->archive_next;
|
|
element = bfd_openr_next_archived_file (ibfd, element);
|
|
}
|
|
}
|
|
|
|
bfd_set_format (obfd, bfd_archive);
|
|
|
|
obfd->has_armap = 1;
|
|
obfd->is_thin_archive = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ar_addlib_doer (bfd *abfd, bfd *prev)
|
|
{
|
|
/* Add this module to the output bfd. */
|
|
if (prev != NULL)
|
|
prev->archive_next = abfd->archive_next;
|
|
|
|
abfd->archive_next = obfd->archive_head;
|
|
obfd->archive_head = abfd;
|
|
}
|
|
|
|
void
|
|
ar_addlib (char *name, struct list *list)
|
|
{
|
|
if (obfd == NULL)
|
|
{
|
|
fprintf (stderr, _("%s: no output archive specified yet\n"), program_name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
bfd *arch;
|
|
|
|
arch = open_inarch (name, (char *) NULL);
|
|
if (arch != NULL)
|
|
map_over_list (arch, ar_addlib_doer, list);
|
|
|
|
/* Don't close the bfd, since it will make the elements disappear. */
|
|
}
|
|
}
|
|
|
|
void
|
|
ar_addmod (struct list *list)
|
|
{
|
|
if (!obfd)
|
|
{
|
|
fprintf (stderr, _("%s: no open output archive\n"), program_name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
while (list)
|
|
{
|
|
bfd *abfd;
|
|
|
|
#if BFD_SUPPORTS_PLUGINS
|
|
abfd = bfd_openr (list->name, "plugin");
|
|
#else
|
|
abfd = bfd_openr (list->name, NULL);
|
|
#endif
|
|
if (!abfd)
|
|
{
|
|
fprintf (stderr, _("%s: can't open file %s\n"),
|
|
program_name, list->name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
abfd->archive_next = obfd->archive_head;
|
|
obfd->archive_head = abfd;
|
|
}
|
|
list = list->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ar_clear (void)
|
|
{
|
|
if (obfd)
|
|
obfd->archive_head = 0;
|
|
}
|
|
|
|
void
|
|
ar_delete (struct list *list)
|
|
{
|
|
if (!obfd)
|
|
{
|
|
fprintf (stderr, _("%s: no open output archive\n"), program_name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
while (list)
|
|
{
|
|
/* Find this name in the archive. */
|
|
bfd *member = obfd->archive_head;
|
|
bfd **prev = &(obfd->archive_head);
|
|
int found = 0;
|
|
|
|
while (member)
|
|
{
|
|
if (FILENAME_CMP (bfd_get_filename (member), list->name) == 0)
|
|
{
|
|
*prev = member->archive_next;
|
|
found = 1;
|
|
}
|
|
else
|
|
prev = &(member->archive_next);
|
|
|
|
member = member->archive_next;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
fprintf (stderr, _("%s: can't find module file %s\n"),
|
|
program_name, list->name);
|
|
maybequit ();
|
|
}
|
|
|
|
list = list->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ar_save (void)
|
|
{
|
|
if (!obfd)
|
|
{
|
|
fprintf (stderr, _("%s: no open output archive\n"), program_name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
char *ofilename = xstrdup (bfd_get_filename (obfd));
|
|
bfd_boolean skip_stat = FALSE;
|
|
struct stat target_stat;
|
|
int ofd = -1;
|
|
|
|
if (deterministic > 0)
|
|
obfd->flags |= BFD_DETERMINISTIC_OUTPUT;
|
|
|
|
#if !defined (_WIN32) || defined (__CYGWIN32__)
|
|
/* It's OK to fail; at worst it will result in SMART_RENAME using a slow
|
|
copy fallback to write the output. */
|
|
ofd = dup (fileno (obfd->iostream));
|
|
if (lstat (real_name, &target_stat) != 0)
|
|
skip_stat = TRUE;
|
|
#endif
|
|
|
|
bfd_close (obfd);
|
|
|
|
smart_rename (ofilename, real_name, ofd,
|
|
skip_stat ? NULL : &target_stat, 0);
|
|
obfd = 0;
|
|
free (ofilename);
|
|
}
|
|
}
|
|
|
|
void
|
|
ar_replace (struct list *list)
|
|
{
|
|
if (!obfd)
|
|
{
|
|
fprintf (stderr, _("%s: no open output archive\n"), program_name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
while (list)
|
|
{
|
|
/* Find this name in the archive. */
|
|
bfd *member = obfd->archive_head;
|
|
bfd **prev = &(obfd->archive_head);
|
|
int found = 0;
|
|
|
|
while (member)
|
|
{
|
|
if (FILENAME_CMP (bfd_get_filename (member), list->name) == 0)
|
|
{
|
|
/* Found the one to replace. */
|
|
bfd *abfd = bfd_openr (list->name, NULL);
|
|
|
|
if (!abfd)
|
|
{
|
|
fprintf (stderr, _("%s: can't open file %s\n"),
|
|
program_name, list->name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
*prev = abfd;
|
|
abfd->archive_next = member->archive_next;
|
|
found = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prev = &(member->archive_next);
|
|
}
|
|
member = member->archive_next;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
bfd *abfd = bfd_openr (list->name, NULL);
|
|
|
|
fprintf (stderr,_("%s: can't find module file %s\n"),
|
|
program_name, list->name);
|
|
if (!abfd)
|
|
{
|
|
fprintf (stderr, _("%s: can't open file %s\n"),
|
|
program_name, list->name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
*prev = abfd;
|
|
}
|
|
|
|
list = list->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* And I added this one. */
|
|
void
|
|
ar_list (void)
|
|
{
|
|
if (!obfd)
|
|
{
|
|
fprintf (stderr, _("%s: no open output archive\n"), program_name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
bfd *abfd;
|
|
|
|
outfile = stdout;
|
|
verbose =1 ;
|
|
printf (_("Current open archive is %s\n"), bfd_get_filename (obfd));
|
|
|
|
for (abfd = obfd->archive_head;
|
|
abfd != (bfd *)NULL;
|
|
abfd = abfd->archive_next)
|
|
ar_directory_doer (abfd, (bfd *) NULL);
|
|
}
|
|
}
|
|
|
|
void
|
|
ar_end (void)
|
|
{
|
|
if (obfd)
|
|
{
|
|
bfd_cache_close (obfd);
|
|
unlink (bfd_get_filename (obfd));
|
|
}
|
|
}
|
|
|
|
void
|
|
ar_extract (struct list *list)
|
|
{
|
|
if (!obfd)
|
|
{
|
|
fprintf (stderr, _("%s: no open archive\n"), program_name);
|
|
maybequit ();
|
|
}
|
|
else
|
|
{
|
|
while (list)
|
|
{
|
|
/* Find this name in the archive. */
|
|
bfd *member = obfd->archive_head;
|
|
int found = 0;
|
|
|
|
while (member && !found)
|
|
{
|
|
if (FILENAME_CMP (bfd_get_filename (member), list->name) == 0)
|
|
{
|
|
extract_file (member);
|
|
found = 1;
|
|
}
|
|
|
|
member = member->archive_next;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
bfd_openr (list->name, NULL);
|
|
fprintf (stderr, _("%s: can't find module file %s\n"),
|
|
program_name, list->name);
|
|
}
|
|
|
|
list = list->next;
|
|
}
|
|
}
|
|
}
|