binutils-gdb/binutils/arsup.c
Siddhesh Poyarekar 014cc7f849 binutils: Make smart_rename safe too
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.
2020-12-07 20:48:33 +05:30

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;
}
}
}