/* install - copy files and set attributes
- Copyright (C) 89, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <signal.h>
#include <pwd.h>
#include <grp.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "backupfile.h"
#include "mkdir-p.h"
#include "modechange.h"
#include "quote.h"
+#include "quotearg.h"
#include "savewd.h"
#include "stat-time.h"
#include "utimens.h"
# include <sys/wait.h>
#endif
+static int selinux_enabled = 0;
+static bool use_default_selinux_context = true;
+
#if ! HAVE_ENDGRENT
# define endgrent() ((void) 0)
#endif
/* Number of bytes of a file to copy at a time. */
#define READ_SIZE (32 * 1024)
-/* Options passed to subsidiary functions. */
-struct install_options
-{
- /* Full name of file being installed. */
- char const *full_name;
-
- /* Options for cp-related code. */
- struct cp_options cp;
-};
-
static bool change_timestamps (struct stat const *from_sb, char const *to);
static bool change_attributes (char const *name);
static bool copy_file (const char *from, const char *to,
const struct cp_options *x);
static bool install_file_in_file_parents (char const *from, char *to,
- struct install_options *x);
+ struct cp_options *x);
static bool install_file_in_dir (const char *from, const char *to_dir,
const struct cp_options *x);
static bool install_file_in_file (const char *from, const char *to,
static void get_ids (void);
static void strip (char const *name);
static void announce_mkdir (char const *dir, void *options);
-static int make_ancestor (char const *dir, void *options);
+static int make_ancestor (char const *dir, char const *component,
+ void *options);
void usage (int status);
/* The name this program was run with, for error messages. */
/* If true, install a directory instead of a regular file. */
static bool dir_arg;
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ PRESERVE_CONTEXT_OPTION = CHAR_MAX + 1
+};
+
static struct option const long_options[] =
{
{"backup", optional_argument, NULL, 'b'},
+ {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{"directory", no_argument, NULL, 'd'},
{"group", required_argument, NULL, 'g'},
{"mode", required_argument, NULL, 'm'},
{"no-target-directory", no_argument, NULL, 'T'},
{"owner", required_argument, NULL, 'o'},
{"preserve-timestamps", no_argument, NULL, 'p'},
+ {"preserve-context", no_argument, NULL, PRESERVE_CONTEXT_OPTION},
+ /* Continue silent support for --preserve_context until Jan 2008. FIXME-obs
+ After that, FIXME-obs: warn in, say, late 2008, and disable altogether
+ a year or two later. */
+ {"preserve_context", no_argument, NULL, PRESERVE_CONTEXT_OPTION},
{"strip", no_argument, NULL, 's'},
{"suffix", required_argument, NULL, 'S'},
{"target-directory", required_argument, NULL, 't'},
x->preserve_mode = false;
x->preserve_timestamps = false;
x->require_preserve = false;
+ x->require_preserve_context = false;
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
x->stdin_tty = false;
x->update = false;
+ x->preserve_security_context = false;
x->verbose = false;
x->dest_info = NULL;
x->src_info = NULL;
}
+/* Modify file context to match the specified policy.
+ If an error occurs the file will remain with the default directory
+ context. */
+static void
+setdefaultfilecon (char const *file)
+{
+ struct stat st;
+ security_context_t scontext = NULL;
+ if (selinux_enabled != 1)
+ {
+ /* Indicate no context found. */
+ return;
+ }
+ if (lstat (file, &st) != 0)
+ return;
+
+ /* If there's an error determining the context, or it has none,
+ return to allow default context */
+ if ((matchpathcon (file, st.st_mode, &scontext) != 0) ||
+ (strcmp (scontext, "<<none>>") == 0))
+ {
+ if (scontext != NULL)
+ freecon (scontext);
+ return;
+ }
+
+ if (lsetfilecon (file, scontext) < 0 && errno != ENOTSUP)
+ error (0, errno,
+ _("warning: %s: failed to change context to %s"),
+ quotearg_colon (file), scontext);
+
+ freecon (scontext);
+ return;
+}
+
/* FILE is the last operand of this command. Return true if FILE is a
directory. But report an error there is a problem accessing FILE,
or if FILE does not exist but would have to refer to an existing
static int
process_dir (char *dir, struct savewd *wd, void *options)
{
- struct install_options *o = options;
- o->full_name = dir;
return (make_dir_parents (dir, wd,
make_ancestor, options,
dir_mode, announce_mkdir,
char *backup_suffix_string;
char *version_control_string = NULL;
bool mkdir_and_install = false;
- struct install_options x;
+ struct cp_options x;
char const *target_directory = NULL;
bool no_target_directory = false;
int n_files;
char **file;
+ security_context_t scontext = NULL;
+ /* set iff kernel has extra selinux system calls */
+ selinux_enabled = (0 < is_selinux_enabled ());
initialize_main (&argc, &argv);
program_name = argv[0];
atexit (close_stdout);
- cp_option_init (&x.cp);
+ cp_option_init (&x);
owner_name = NULL;
group_name = NULL;
we'll actually use backup_suffix_string. */
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
- while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pt:TvS:", long_options,
+ while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pt:TvS:Z:", long_options,
NULL)) != -1)
{
switch (optc)
mkdir_and_install = true;
break;
case 'v':
- x.cp.verbose = true;
+ x.verbose = true;
break;
case 'g':
group_name = optarg;
owner_name = optarg;
break;
case 'p':
- x.cp.preserve_timestamps = true;
+ x.preserve_timestamps = true;
break;
case 'S':
make_backups = true;
case 'T':
no_target_directory = true;
break;
+
+ case PRESERVE_CONTEXT_OPTION:
+ if ( ! selinux_enabled)
+ {
+ error (0, 0, _("Warning: ignoring --preserve-context; "
+ "this kernel is not SELinux-enabled."));
+ break;
+ }
+ x.preserve_security_context = true;
+ use_default_selinux_context = false;
+ break;
+ case 'Z':
+ if ( ! selinux_enabled)
+ {
+ error (0, 0, _("Warning: ignoring --context (-Z); "
+ "this kernel is not SELinux-enabled."));
+ break;
+ }
+ scontext = optarg;
+ use_default_selinux_context = false;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
error (EXIT_FAILURE, 0,
_("target directory not allowed when installing a directory"));
+ if (x.preserve_security_context && scontext != NULL)
+ error (EXIT_FAILURE, 0,
+ _("cannot force target context to %s and preserve it"),
+ quote (scontext));
+
if (backup_suffix_string)
simple_backup_suffix = xstrdup (backup_suffix_string);
- x.cp.backup_type = (make_backups
- ? xget_version (_("backup type"),
- version_control_string)
- : no_backups);
+ x.backup_type = (make_backups
+ ? xget_version (_("backup type"),
+ version_control_string)
+ : no_backups);
+
+ if (scontext && setfscreatecon (scontext) < 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set default file creation context to %s"),
+ quote (scontext));
n_files = argc - optind;
file = argv + optind;
{
if (! (mkdir_and_install
? install_file_in_file_parents (file[0], file[1], &x)
- : install_file_in_file (file[0], file[1], &x.cp)))
+ : install_file_in_file (file[0], file[1], &x)))
exit_status = EXIT_FAILURE;
}
else
{
int i;
- dest_info_init (&x.cp);
+ dest_info_init (&x);
for (i = 0; i < n_files; i++)
- if (! install_file_in_dir (file[i], target_directory, &x.cp))
+ if (! install_file_in_dir (file[i], target_directory, &x))
exit_status = EXIT_FAILURE;
}
}
static bool
install_file_in_file_parents (char const *from, char *to,
- struct install_options *x)
+ struct cp_options *x)
{
bool save_working_directory =
! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
}
}
- return (status == EXIT_SUCCESS && install_file_in_file (from, to, &x->cp));
+ return (status == EXIT_SUCCESS && install_file_in_file (from, to, x));
}
/* Copy file FROM onto file TO and give TO the appropriate
return false;
if (strip_files)
strip (to);
- if (! change_attributes (to))
+ if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode))
+ && ! change_timestamps (&from_sb, to))
return false;
- if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode)))
- return change_timestamps (&from_sb, to);
- return true;
+ return change_attributes (to);
}
/* Copy file FROM into directory TO_DIR, keeping its same name,
static bool
change_attributes (char const *name)
{
+ bool ok = false;
/* chown must precede chmod because on some systems,
chown clears the set[ug]id bits for non-superusers,
resulting in incorrect permissions.
else if (chmod (name, mode) != 0)
error (0, errno, _("cannot change permissions of %s"), quote (name));
else
- return true;
+ ok = true;
- return false;
+ if (use_default_selinux_context)
+ setdefaultfilecon (name);
+
+ return ok;
}
/* Set the timestamps of file TO to match those of file FROM.
error (EXIT_FAILURE, errno, _("cannot run strip"));
break;
default: /* Parent. */
- /* Parent process. */
- while (pid != wait (&status)) /* Wait for kid to finish. */
- /* Do nothing. */ ;
- if (status)
- error (EXIT_FAILURE, 0, _("strip failed"));
+ if (waitpid (pid, &status, 0) < 0)
+ error (EXIT_FAILURE, errno, _("waiting for strip"));
+ else if (! WIFEXITED (status) || WEXITSTATUS (status))
+ error (EXIT_FAILURE, 0, _("strip process terminated abnormally"));
break;
}
}
static void
announce_mkdir (char const *dir, void *options)
{
- struct install_options const *x = options;
- if (x->cp.verbose)
+ struct cp_options const *x = options;
+ if (x->verbose)
error (0, 0, _("creating directory %s"), quote (dir));
}
-/* Make ancestor directory DIR, with options OPTIONS. */
+/* Make ancestor directory DIR, whose last file name component is
+ COMPONENT, with options OPTIONS. Assume the working directory is
+ COMPONENT's parent. */
static int
-make_ancestor (char const *dir, void *options)
+make_ancestor (char const *dir, char const *component, void *options)
{
- struct install_options const *x = options;
- int r = mkdir (dir, DEFAULT_MODE);
+ int r = mkdir (component, DEFAULT_MODE);
if (r == 0)
- announce_mkdir (x->full_name, options);
+ announce_mkdir (dir, options);
return r;
}
-p, --preserve-timestamps apply access/modification times of SOURCE files\n\
to corresponding destination files\n\
-s, --strip strip symbol tables\n\
- -S, --suffix=SUFFIX override the usual backup suffix\n\
+ -S, --suffix=SUFFIX override the usual backup suffix\n\
-t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
-T, --no-target-directory treat DEST as a normal file\n\
-v, --verbose print the name of each directory as it is created\n\
"), stdout);
+ fputs (_("\
+ --preserve-context preserve SELinux security context\n\
+ -Z, --context=CONTEXT set SELinux security context of files and directories\n\
+"), stdout);
+
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
existing, nil numbered if numbered backups exist, simple otherwise\n\
simple, never always make simple backups\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_bug_reporting_address ();
}
exit (status);
}