X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Finstall.c;h=cefee0c916a67d195f17299072a63dd8a5562806;hb=77da73c75432f3c5b4beebae7b0797a1e33160bc;hp=cebb64278fab1367bc2446dadc0e53dc21c6103c;hpb=e1aaf8903db97f3240b1551fd6936ccdc652dfc8;p=platform%2Fupstream%2Fcoreutils.git diff --git a/src/install.c b/src/install.c index cebb642..cefee0c 100644 --- a/src/install.c +++ b/src/install.c @@ -1,5 +1,5 @@ /* install - copy files and set attributes - Copyright (C) 1989-1991, 1995-2011 Free Software Foundation, Inc. + Copyright (C) 1989-2013 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 @@ -44,7 +44,7 @@ #include "utimens.h" #include "xstrtol.h" -/* The official name of this program (e.g., no `g' prefix). */ +/* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "install" #define AUTHORS proper_name ("David MacKenzie") @@ -68,35 +68,18 @@ static bool use_default_selinux_context = true; # define matchpathcon_init_prefix(a, p) /* empty */ #endif -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 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, - const struct cp_options *x); -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, char const *component, - void *options); -void usage (int status); - /* The user name that will own the files, or NULL to make the owner the current user ID. */ static char *owner_name; -/* The user ID corresponding to `owner_name'. */ +/* The user ID corresponding to 'owner_name'. */ static uid_t owner_id; /* The group name that will own the files, or NULL to make the group the current group ID. */ static char *group_name; -/* The group ID corresponding to `group_name'. */ +/* The group ID corresponding to 'group_name'. */ static gid_t group_id; #define DEFAULT_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) @@ -131,7 +114,6 @@ static char const *strip_program = "strip"; enum { PRESERVE_CONTEXT_OPTION = CHAR_MAX + 1, - PRESERVE_CONTEXT_OPTION_DEPRECATED, STRIP_PROGRAM_OPTION }; @@ -147,9 +129,6 @@ static struct option const long_options[] = {"owner", required_argument, NULL, 'o'}, {"preserve-timestamps", no_argument, NULL, 'p'}, {"preserve-context", no_argument, NULL, PRESERVE_CONTEXT_OPTION}, - /* --preserve_context was silently supported until Apr 2009. - FIXME: disable altogether in a year or so. */ - {"preserve_context", no_argument, NULL, PRESERVE_CONTEXT_OPTION_DEPRECATED}, {"strip", no_argument, NULL, 's'}, {"strip-program", required_argument, NULL, STRIP_PROGRAM_OPTION}, {"suffix", required_argument, NULL, 'S'}, @@ -213,9 +192,27 @@ need_copy (const char *src_name, const char *dest_name, return true; if (src_sb.st_size != dest_sb.st_size - || (dest_sb.st_mode & CHMOD_MODE_BITS) != mode - || dest_sb.st_uid != (owner_id == (uid_t) -1 ? getuid () : owner_id) - || dest_sb.st_gid != (group_id == (gid_t) -1 ? getgid () : group_id)) + || (dest_sb.st_mode & CHMOD_MODE_BITS) != mode) + return true; + + if (owner_id == (uid_t) -1) + { + errno = 0; + uid_t ruid = getuid (); + if ((ruid == (uid_t) -1 && errno) || dest_sb.st_uid != ruid) + return true; + } + else if (dest_sb.st_uid != owner_id) + return true; + + if (group_id == (uid_t) -1) + { + errno = 0; + gid_t rgid = getgid (); + if ((rgid == (uid_t) -1 && errno) || dest_sb.st_gid != rgid) + return true; + } + else if (dest_sb.st_gid != group_id) return true; /* compare SELinux context if preserving */ @@ -278,6 +275,7 @@ cp_option_init (struct cp_options *x) x->preserve_links = false; x->preserve_mode = false; x->preserve_timestamps = false; + x->explicit_no_preserve_mode = false; x->reduce_diagnostics=false; x->data_copy_required = true; x->require_preserve = false; @@ -362,8 +360,8 @@ setdefaultfilecon (char const *file) /* 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) || - STREQ (scontext, "<>")) + if ((matchpathcon (file, st.st_mode, &scontext) != 0) + || STREQ (scontext, "<>")) { if (scontext != NULL) freecon (scontext); @@ -407,6 +405,27 @@ target_directory_operand (char const *file) return is_a_dir; } +/* Report that directory DIR was made, if OPTIONS requests this. */ +static void +announce_mkdir (char const *dir, void *options) +{ + struct cp_options const *x = options; + if (x->verbose) + prog_fprintf (stdout, _("creating directory %s"), quote (dir)); +} + +/* 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, char const *component, void *options) +{ + int r = mkdir (component, DEFAULT_MODE); + if (r == 0) + announce_mkdir (dir, options); + return r; +} + /* Process a command-line file name, for the -d option. */ static int process_dir (char *dir, struct savewd *wd, void *options) @@ -419,267 +438,255 @@ process_dir (char *dir, struct savewd *wd, void *options) : EXIT_FAILURE); } -int -main (int argc, char **argv) -{ - int optc; - int exit_status = EXIT_SUCCESS; - const char *specified_mode = NULL; - bool make_backups = false; - char *backup_suffix_string; - char *version_control_string = NULL; - bool mkdir_and_install = false; - struct cp_options x; - char const *target_directory = NULL; - bool no_target_directory = false; - int n_files; - char **file; - bool strip_program_specified = false; - security_context_t scontext = NULL; - /* set iff kernel has extra selinux system calls */ - selinux_enabled = (0 < is_selinux_enabled ()); - - initialize_main (&argc, &argv); - set_program_name (argv[0]); - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); +/* Copy file FROM onto file TO, creating TO if necessary. + Return true if successful. */ - atexit (close_stdin); +static bool +copy_file (const char *from, const char *to, const struct cp_options *x) +{ + bool copy_into_self; - cp_option_init (&x); + if (copy_only_if_needed && !need_copy (from, to, x)) + return true; - owner_name = NULL; - group_name = NULL; - strip_files = false; - dir_arg = false; - umask (0); + /* Allow installing from non-regular files like /dev/null. + Charles Karney reported that some Sun version of install allows that + and that sendmail's installation process relies on the behavior. + However, since !x->recursive, the call to "copy" will fail if FROM + is a directory. */ - /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless - we'll actually use backup_suffix_string. */ - backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); + return copy (from, to, false, x, ©_into_self, NULL); +} - while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z:", long_options, - NULL)) != -1) - { - switch (optc) - { - case 'b': - make_backups = true; - if (optarg) - version_control_string = optarg; - break; - case 'c': - break; - case 'C': - copy_only_if_needed = true; - break; - case 's': - strip_files = true; -#ifdef SIGCHLD - /* System V fork+wait does not work if SIGCHLD is ignored. */ - signal (SIGCHLD, SIG_DFL); -#endif - break; - case STRIP_PROGRAM_OPTION: - strip_program = xstrdup (optarg); - strip_program_specified = true; - break; - case 'd': - dir_arg = true; - break; - case 'D': - mkdir_and_install = true; - break; - case 'v': - x.verbose = true; - break; - case 'g': - group_name = optarg; - break; - case 'm': - specified_mode = optarg; - break; - case 'o': - owner_name = optarg; - break; - case 'p': - x.preserve_timestamps = true; - break; - case 'S': - make_backups = true; - backup_suffix_string = optarg; - break; - case 't': - if (target_directory) - error (EXIT_FAILURE, 0, - _("multiple target directories specified")); - else - { - struct stat st; - if (stat (optarg, &st) != 0) - error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg)); - if (! S_ISDIR (st.st_mode)) - error (EXIT_FAILURE, 0, _("target %s is not a directory"), - quote (optarg)); - } - target_directory = optarg; - break; - case 'T': - no_target_directory = true; - break; +/* Set the attributes of file or directory NAME. + Return true if successful. */ - case PRESERVE_CONTEXT_OPTION_DEPRECATED: - error (0, 0, _("WARNING: --preserve_context is deprecated; " - "use --preserve-context instead")); - /* fall through */ - 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: - usage (EXIT_FAILURE); - } - } +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. + On System V, users can give away files with chown and then not + be able to chmod them. So don't give files away. - /* Check for invalid combinations of arguments. */ - if (dir_arg && strip_files) - error (EXIT_FAILURE, 0, - _("the strip option may not be used when installing a directory")); - if (dir_arg && target_directory) - error (EXIT_FAILURE, 0, - _("target directory not allowed when installing a directory")); + We don't normally ignore errors from chown because the idea of + the install command is that the file is supposed to end up with + precisely the attributes that the user specified (or defaulted). + If the file doesn't end up with the group they asked for, they'll + want to know. */ - if (x.preserve_security_context && scontext != NULL) - error (EXIT_FAILURE, 0, - _("cannot force target context to %s and preserve it"), - quote (scontext)); + if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1) + && lchown (name, owner_id, group_id) != 0) + error (0, errno, _("cannot change ownership of %s"), quote (name)); + else if (chmod (name, mode) != 0) + error (0, errno, _("cannot change permissions of %s"), quote (name)); + else + ok = true; - if (backup_suffix_string) - simple_backup_suffix = xstrdup (backup_suffix_string); + if (use_default_selinux_context) + setdefaultfilecon (name); - x.backup_type = (make_backups - ? xget_version (_("backup type"), - version_control_string) - : no_backups); + return ok; +} - if (scontext && setfscreatecon (scontext) < 0) - error (EXIT_FAILURE, errno, - _("failed to set default file creation context to %s"), - quote (scontext)); +/* Set the timestamps of file DEST to match those of SRC_SB. + Return true if successful. */ - n_files = argc - optind; - file = argv + optind; +static bool +change_timestamps (struct stat const *src_sb, char const *dest) +{ + struct timespec timespec[2]; + timespec[0] = get_stat_atime (src_sb); + timespec[1] = get_stat_mtime (src_sb); - if (n_files <= ! (dir_arg || target_directory)) + if (utimens (dest, timespec)) { - if (n_files <= 0) - error (0, 0, _("missing file operand")); - else - error (0, 0, _("missing destination file operand after %s"), - quote (file[0])); - usage (EXIT_FAILURE); + error (0, errno, _("cannot set time stamps for %s"), quote (dest)); + return false; } + return true; +} - if (no_target_directory) +/* Strip the symbol table from the file NAME. + We could dig the magic number out of the file first to + determine whether to strip it, but the header files and + magic numbers vary so much from system to system that making + it portable would be very difficult. Not worth the effort. */ + +static void +strip (char const *name) +{ + int status; + pid_t pid = fork (); + + switch (pid) { - if (target_directory) - error (EXIT_FAILURE, 0, - _("cannot combine --target-directory (-t) " - "and --no-target-directory (-T)")); - if (2 < n_files) - { - error (0, 0, _("extra operand %s"), quote (file[2])); - usage (EXIT_FAILURE); - } - } - else if (! (dir_arg || target_directory)) - { - if (2 <= n_files && target_directory_operand (file[n_files - 1])) - target_directory = file[--n_files]; - else if (2 < n_files) - error (EXIT_FAILURE, 0, _("target %s is not a directory"), - quote (file[n_files - 1])); + case -1: + error (EXIT_FAILURE, errno, _("fork system call failed")); + break; + case 0: /* Child. */ + execlp (strip_program, strip_program, name, NULL); + error (EXIT_FAILURE, errno, _("cannot run %s"), strip_program); + break; + default: /* Parent. */ + 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; } +} - if (specified_mode) - { - struct mode_change *change = mode_compile (specified_mode); - if (!change) - error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode)); - mode = mode_adjust (0, false, 0, change, NULL); - dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits); - free (change); - } +/* Initialize the user and group ownership of the files to install. */ - if (strip_program_specified && !strip_files) - error (0, 0, _("WARNING: ignoring --strip-program option as -s option was " - "not specified")); +static void +get_ids (void) +{ + struct passwd *pw; + struct group *gr; - if (copy_only_if_needed && x.preserve_timestamps) + if (owner_name) { - error (0, 0, _("options --compare (-C) and --preserve-timestamps are " - "mutually exclusive")); - usage (EXIT_FAILURE); + pw = getpwnam (owner_name); + if (pw == NULL) + { + unsigned long int tmp; + if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK + || UID_T_MAX < tmp) + error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name)); + owner_id = tmp; + } + else + owner_id = pw->pw_uid; + endpwent (); } + else + owner_id = (uid_t) -1; - if (copy_only_if_needed && strip_files) + if (group_name) { - error (0, 0, _("options --compare (-C) and --strip are mutually " - "exclusive")); - usage (EXIT_FAILURE); + gr = getgrnam (group_name); + if (gr == NULL) + { + unsigned long int tmp; + if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK + || GID_T_MAX < tmp) + error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name)); + group_id = tmp; + } + else + group_id = gr->gr_gid; + endgrent (); } + else + group_id = (gid_t) -1; +} - if (copy_only_if_needed && extra_mode (mode)) - error (0, 0, _("the --compare (-C) option is ignored when you" - " specify a mode with non-permission bits")); - - get_ids (); - - if (dir_arg) - exit_status = savewd_process_files (n_files, file, process_dir, &x); +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + emit_try_help (); else { - /* FIXME: it's a little gross that this initialization is - required by copy.c::copy. */ - hash_init (); + printf (_("\ +Usage: %s [OPTION]... [-T] SOURCE DEST\n\ + or: %s [OPTION]... SOURCE... DIRECTORY\n\ + or: %s [OPTION]... -t DIRECTORY SOURCE...\n\ + or: %s [OPTION]... -d DIRECTORY...\n\ +"), + program_name, program_name, program_name, program_name); + fputs (_("\ +\n\ +This install program copies files (often just compiled) into destination\n\ +locations you choose. If you want to download and install a ready-to-use\n\ +package on a GNU/Linux system, you should instead be using a package manager\n\ +like yum(1) or apt-get(1).\n\ +\n\ +In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\ +the existing DIRECTORY, while setting permission modes and owner/group.\n\ +In the 4th form, create all components of the given DIRECTORY(ies).\n\ +\n\ +"), stdout); + fputs (_("\ +Mandatory arguments to long options are mandatory for short options too.\n\ +"), stdout); + fputs (_("\ + --backup[=CONTROL] make a backup of each existing destination file\n\ + -b like --backup but does not accept an argument\n\ + -c (ignored)\n\ + -C, --compare compare each pair of source and destination files, and\n\ + in some cases, do not modify the destination at all\n\ + -d, --directory treat all arguments as directory names; create all\n\ + components of the specified directories\n\ +"), stdout); + fputs (_("\ + -D create all leading components of DEST except the last,\n\ + then copy SOURCE to DEST\n\ + -g, --group=GROUP set group ownership, instead of process' current group\n\ + -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\ + -o, --owner=OWNER set ownership (super-user only)\n\ +"), stdout); + fputs (_("\ + -p, --preserve-timestamps apply access/modification times of SOURCE files\n\ + to corresponding destination files\n\ + -s, --strip strip symbol tables\n\ + --strip-program=PROGRAM program used to strip binaries\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); - if (!target_directory) - { - if (! (mkdir_and_install - ? install_file_in_file_parents (file[0], file[1], &x) - : install_file_in_file (file[0], file[1], &x))) - exit_status = EXIT_FAILURE; - } - else - { - int i; - dest_info_init (&x); - for (i = 0; i < n_files; i++) - if (! install_file_in_dir (file[i], target_directory, &x)) - exit_status = EXIT_FAILURE; - } + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + fputs (_("\ +\n\ +The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\ +The version control method may be selected via the --backup option or through\n\ +the VERSION_CONTROL environment variable. Here are the values:\n\ +\n\ +"), stdout); + fputs (_("\ + none, off never make backups (even if --backup is given)\n\ + numbered, t make numbered backups\n\ + existing, nil numbered if numbered backups exist, simple otherwise\n\ + simple, never always make simple backups\n\ +"), stdout); + emit_ancillary_info (); } + exit (status); +} - exit (exit_status); +/* Copy file FROM onto file TO and give TO the appropriate + attributes. + Return true if successful. */ + +static bool +install_file_in_file (const char *from, const char *to, + const struct cp_options *x) +{ + struct stat from_sb; + if (x->preserve_timestamps && stat (from, &from_sb) != 0) + { + error (0, errno, _("cannot stat %s"), quote (from)); + return false; + } + if (! copy_file (from, to, x)) + return false; + if (strip_files) + strip (to); + if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode)) + && ! change_timestamps (&from_sb, to)) + return false; + return change_attributes (to); } /* Copy file FROM onto file TO, creating any missing parent directories of TO. @@ -721,30 +728,6 @@ install_file_in_file_parents (char const *from, char *to, return (status == EXIT_SUCCESS && install_file_in_file (from, to, x)); } -/* Copy file FROM onto file TO and give TO the appropriate - attributes. - Return true if successful. */ - -static bool -install_file_in_file (const char *from, const char *to, - const struct cp_options *x) -{ - struct stat from_sb; - if (x->preserve_timestamps && stat (from, &from_sb) != 0) - { - error (0, errno, _("cannot stat %s"), quote (from)); - return false; - } - if (! copy_file (from, to, x)) - return false; - if (strip_files) - strip (to); - if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode)) - && ! change_timestamps (&from_sb, to)) - return false; - return change_attributes (to); -} - /* Copy file FROM into directory TO_DIR, keeping its same name, and give the copy the appropriate attributes. Return true if successful. */ @@ -760,251 +743,261 @@ install_file_in_dir (const char *from, const char *to_dir, return ret; } -/* Copy file FROM onto file TO, creating TO if necessary. - Return true if successful. */ - -static bool -copy_file (const char *from, const char *to, const struct cp_options *x) +int +main (int argc, char **argv) { - bool copy_into_self; - - if (copy_only_if_needed && !need_copy (from, to, x)) - return true; - - /* Allow installing from non-regular files like /dev/null. - Charles Karney reported that some Sun version of install allows that - and that sendmail's installation process relies on the behavior. - However, since !x->recursive, the call to "copy" will fail if FROM - is a directory. */ - - return copy (from, to, false, x, ©_into_self, NULL); -} - -/* Set the attributes of file or directory NAME. - Return true if successful. */ - -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. - On System V, users can give away files with chown and then not - be able to chmod them. So don't give files away. - - We don't normally ignore errors from chown because the idea of - the install command is that the file is supposed to end up with - precisely the attributes that the user specified (or defaulted). - If the file doesn't end up with the group they asked for, they'll - want to know. */ + int optc; + int exit_status = EXIT_SUCCESS; + const char *specified_mode = NULL; + bool make_backups = false; + char *backup_suffix_string; + char *version_control_string = NULL; + bool mkdir_and_install = false; + struct cp_options x; + char const *target_directory = NULL; + bool no_target_directory = false; + int n_files; + char **file; + bool strip_program_specified = false; + security_context_t scontext = NULL; + /* set iff kernel has extra selinux system calls */ + selinux_enabled = (0 < is_selinux_enabled ()); - if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1) - && lchown (name, owner_id, group_id) != 0) - error (0, errno, _("cannot change ownership of %s"), quote (name)); - else if (chmod (name, mode) != 0) - error (0, errno, _("cannot change permissions of %s"), quote (name)); - else - ok = true; + initialize_main (&argc, &argv); + set_program_name (argv[0]); + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); - if (use_default_selinux_context) - setdefaultfilecon (name); + atexit (close_stdin); - return ok; -} + cp_option_init (&x); -/* Set the timestamps of file TO to match those of file FROM. - Return true if successful. */ + owner_name = NULL; + group_name = NULL; + strip_files = false; + dir_arg = false; + umask (0); -static bool -change_timestamps (struct stat const *from_sb, char const *to) -{ - struct timespec timespec[2]; - timespec[0] = get_stat_atime (from_sb); - timespec[1] = get_stat_mtime (from_sb); + /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless + we'll actually use backup_suffix_string. */ + backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - if (utimens (to, timespec)) + while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z:", long_options, + NULL)) != -1) { - error (0, errno, _("cannot set time stamps for %s"), quote (to)); - return false; + switch (optc) + { + case 'b': + make_backups = true; + if (optarg) + version_control_string = optarg; + break; + case 'c': + break; + case 'C': + copy_only_if_needed = true; + break; + case 's': + strip_files = true; +#ifdef SIGCHLD + /* System V fork+wait does not work if SIGCHLD is ignored. */ + signal (SIGCHLD, SIG_DFL); +#endif + break; + case STRIP_PROGRAM_OPTION: + strip_program = xstrdup (optarg); + strip_program_specified = true; + break; + case 'd': + dir_arg = true; + break; + case 'D': + mkdir_and_install = true; + break; + case 'v': + x.verbose = true; + break; + case 'g': + group_name = optarg; + break; + case 'm': + specified_mode = optarg; + break; + case 'o': + owner_name = optarg; + break; + case 'p': + x.preserve_timestamps = true; + break; + case 'S': + make_backups = true; + backup_suffix_string = optarg; + break; + case 't': + if (target_directory) + error (EXIT_FAILURE, 0, + _("multiple target directories specified")); + else + { + struct stat st; + if (stat (optarg, &st) != 0) + error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg)); + if (! S_ISDIR (st.st_mode)) + error (EXIT_FAILURE, 0, _("target %s is not a directory"), + quote (optarg)); + } + target_directory = optarg; + break; + 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: + usage (EXIT_FAILURE); + } } - return true; -} -/* Strip the symbol table from the file NAME. - We could dig the magic number out of the file first to - determine whether to strip it, but the header files and - magic numbers vary so much from system to system that making - it portable would be very difficult. Not worth the effort. */ + /* Check for invalid combinations of arguments. */ + if (dir_arg && strip_files) + error (EXIT_FAILURE, 0, + _("the strip option may not be used when installing a directory")); + if (dir_arg && target_directory) + error (EXIT_FAILURE, 0, + _("target directory not allowed when installing a directory")); -static void -strip (char const *name) -{ - int status; - pid_t pid = fork (); + if (x.preserve_security_context && scontext != NULL) + error (EXIT_FAILURE, 0, + _("cannot force target context to %s and preserve it"), + quote (scontext)); - switch (pid) - { - case -1: - error (EXIT_FAILURE, errno, _("fork system call failed")); - break; - case 0: /* Child. */ - execlp (strip_program, strip_program, name, NULL); - error (EXIT_FAILURE, errno, _("cannot run %s"), strip_program); - break; - default: /* Parent. */ - 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; - } -} + if (backup_suffix_string) + simple_backup_suffix = xstrdup (backup_suffix_string); -/* Initialize the user and group ownership of the files to install. */ + x.backup_type = (make_backups + ? xget_version (_("backup type"), + version_control_string) + : no_backups); -static void -get_ids (void) -{ - struct passwd *pw; - struct group *gr; + if (scontext && setfscreatecon (scontext) < 0) + error (EXIT_FAILURE, errno, + _("failed to set default file creation context to %s"), + quote (scontext)); - if (owner_name) + n_files = argc - optind; + file = argv + optind; + + if (n_files <= ! (dir_arg || target_directory)) { - pw = getpwnam (owner_name); - if (pw == NULL) - { - unsigned long int tmp; - if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK - || UID_T_MAX < tmp) - error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name)); - owner_id = tmp; - } + if (n_files <= 0) + error (0, 0, _("missing file operand")); else - owner_id = pw->pw_uid; - endpwent (); + error (0, 0, _("missing destination file operand after %s"), + quote (file[0])); + usage (EXIT_FAILURE); } - else - owner_id = (uid_t) -1; - if (group_name) + if (no_target_directory) { - gr = getgrnam (group_name); - if (gr == NULL) + if (target_directory) + error (EXIT_FAILURE, 0, + _("cannot combine --target-directory (-t) " + "and --no-target-directory (-T)")); + if (2 < n_files) { - unsigned long int tmp; - if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK - || GID_T_MAX < tmp) - error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name)); - group_id = tmp; + error (0, 0, _("extra operand %s"), quote (file[2])); + usage (EXIT_FAILURE); } - else - group_id = gr->gr_gid; - endgrent (); } - else - group_id = (gid_t) -1; -} + else if (! (dir_arg || target_directory)) + { + if (2 <= n_files && target_directory_operand (file[n_files - 1])) + target_directory = file[--n_files]; + else if (2 < n_files) + error (EXIT_FAILURE, 0, _("target %s is not a directory"), + quote (file[n_files - 1])); + } -/* Report that directory DIR was made, if OPTIONS requests this. */ -static void -announce_mkdir (char const *dir, void *options) -{ - struct cp_options const *x = options; - if (x->verbose) - prog_fprintf (stdout, _("creating directory %s"), quote (dir)); -} + if (specified_mode) + { + struct mode_change *change = mode_compile (specified_mode); + if (!change) + error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode)); + mode = mode_adjust (0, false, 0, change, NULL); + dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits); + free (change); + } -/* 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, char const *component, void *options) -{ - int r = mkdir (component, DEFAULT_MODE); - if (r == 0) - announce_mkdir (dir, options); - return r; -} + if (strip_program_specified && !strip_files) + error (0, 0, _("WARNING: ignoring --strip-program option as -s option was " + "not specified")); -void -usage (int status) -{ - if (status != EXIT_SUCCESS) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); + if (copy_only_if_needed && x.preserve_timestamps) + { + error (0, 0, _("options --compare (-C) and --preserve-timestamps are " + "mutually exclusive")); + usage (EXIT_FAILURE); + } + + if (copy_only_if_needed && strip_files) + { + error (0, 0, _("options --compare (-C) and --strip are mutually " + "exclusive")); + usage (EXIT_FAILURE); + } + + if (copy_only_if_needed && extra_mode (mode)) + error (0, 0, _("the --compare (-C) option is ignored when you" + " specify a mode with non-permission bits")); + + get_ids (); + + if (dir_arg) + exit_status = savewd_process_files (n_files, file, process_dir, &x); else { - printf (_("\ -Usage: %s [OPTION]... [-T] SOURCE DEST\n\ - or: %s [OPTION]... SOURCE... DIRECTORY\n\ - or: %s [OPTION]... -t DIRECTORY SOURCE...\n\ - or: %s [OPTION]... -d DIRECTORY...\n\ -"), - program_name, program_name, program_name, program_name); - fputs (_("\ -\n\ -This install program copies files (often just compiled) into destination\n\ -locations you choose. If you want to download and install a ready-to-use\n\ -package on a GNU/Linux system, you should instead be using a package manager\n\ -like yum(1) or apt-get(1).\n\ -\n\ -In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\ -the existing DIRECTORY, while setting permission modes and owner/group.\n\ -In the 4th form, create all components of the given DIRECTORY(ies).\n\ -\n\ -"), stdout); - fputs (_("\ -Mandatory arguments to long options are mandatory for short options too.\n\ -"), stdout); - fputs (_("\ - --backup[=CONTROL] make a backup of each existing destination file\n\ - -b like --backup but does not accept an argument\n\ - -c (ignored)\n\ - -C, --compare compare each pair of source and destination files, and\n\ - in some cases, do not modify the destination at all\n\ - -d, --directory treat all arguments as directory names; create all\n\ - components of the specified directories\n\ -"), stdout); - fputs (_("\ - -D create all leading components of DEST except the last,\n\ - then copy SOURCE to DEST\n\ - -g, --group=GROUP set group ownership, instead of process' current group\n\ - -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\ - -o, --owner=OWNER set ownership (super-user only)\n\ -"), stdout); - fputs (_("\ - -p, --preserve-timestamps apply access/modification times of SOURCE files\n\ - to corresponding destination files\n\ - -s, --strip strip symbol tables\n\ - --strip-program=PROGRAM program used to strip binaries\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); + /* FIXME: it's a little gross that this initialization is + required by copy.c::copy. */ + hash_init (); - fputs (HELP_OPTION_DESCRIPTION, stdout); - fputs (VERSION_OPTION_DESCRIPTION, stdout); - fputs (_("\ -\n\ -The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\ -The version control method may be selected via the --backup option or through\n\ -the VERSION_CONTROL environment variable. Here are the values:\n\ -\n\ -"), stdout); - fputs (_("\ - none, off never make backups (even if --backup is given)\n\ - numbered, t make numbered backups\n\ - existing, nil numbered if numbered backups exist, simple otherwise\n\ - simple, never always make simple backups\n\ -"), stdout); - emit_ancillary_info (); + if (!target_directory) + { + if (! (mkdir_and_install + ? install_file_in_file_parents (file[0], file[1], &x) + : install_file_in_file (file[0], file[1], &x))) + exit_status = EXIT_FAILURE; + } + else + { + int i; + dest_info_init (&x); + for (i = 0; i < n_files; i++) + if (! install_file_in_dir (file[i], target_directory, &x)) + exit_status = EXIT_FAILURE; + } } - exit (status); + + exit (exit_status); }