1 /* install - copy files and set attributes
2 Copyright (C) 1989-2012 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
22 #include <sys/types.h>
26 #include <selinux/selinux.h>
30 #include "backupfile.h"
34 #include "filenamecat.h"
35 #include "full-read.h"
36 #include "mkancesdirs.h"
38 #include "modechange.h"
39 #include "prog-fprintf.h"
43 #include "stat-time.h"
47 /* The official name of this program (e.g., no 'g' prefix). */
48 #define PROGRAM_NAME "install"
50 #define AUTHORS proper_name ("David MacKenzie")
52 static int selinux_enabled
= 0;
53 static bool use_default_selinux_context
= true;
56 # define endgrent() ((void) 0)
60 # define endpwent() ((void) 0)
64 # define lchown(name, uid, gid) chown (name, uid, gid)
67 #if ! HAVE_MATCHPATHCON_INIT_PREFIX
68 # define matchpathcon_init_prefix(a, p) /* empty */
71 /* The user name that will own the files, or NULL to make the owner
72 the current user ID. */
73 static char *owner_name
;
75 /* The user ID corresponding to 'owner_name'. */
76 static uid_t owner_id
;
78 /* The group name that will own the files, or NULL to make the group
79 the current group ID. */
80 static char *group_name
;
82 /* The group ID corresponding to 'group_name'. */
83 static gid_t group_id
;
85 #define DEFAULT_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
87 /* The file mode bits to which non-directory files will be set. The umask has
89 static mode_t mode
= DEFAULT_MODE
;
91 /* Similar, but for directories. */
92 static mode_t dir_mode
= DEFAULT_MODE
;
94 /* The file mode bits that the user cares about. This should be a
95 superset of DIR_MODE and a subset of CHMOD_MODE_BITS. This matters
96 for directories, since otherwise directories may keep their S_ISUID
98 static mode_t dir_mode_bits
= CHMOD_MODE_BITS
;
100 /* Compare files before installing (-C) */
101 static bool copy_only_if_needed
;
103 /* If true, strip executable files after copying them. */
104 static bool strip_files
;
106 /* If true, install a directory instead of a regular file. */
109 /* Program used to strip binaries, "strip" is default */
110 static char const *strip_program
= "strip";
112 /* For long options that have no equivalent short option, use a
113 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
116 PRESERVE_CONTEXT_OPTION
= CHAR_MAX
+ 1,
120 static struct option
const long_options
[] =
122 {"backup", optional_argument
, NULL
, 'b'},
123 {"compare", no_argument
, NULL
, 'C'},
124 {GETOPT_SELINUX_CONTEXT_OPTION_DECL
},
125 {"directory", no_argument
, NULL
, 'd'},
126 {"group", required_argument
, NULL
, 'g'},
127 {"mode", required_argument
, NULL
, 'm'},
128 {"no-target-directory", no_argument
, NULL
, 'T'},
129 {"owner", required_argument
, NULL
, 'o'},
130 {"preserve-timestamps", no_argument
, NULL
, 'p'},
131 {"preserve-context", no_argument
, NULL
, PRESERVE_CONTEXT_OPTION
},
132 {"strip", no_argument
, NULL
, 's'},
133 {"strip-program", required_argument
, NULL
, STRIP_PROGRAM_OPTION
},
134 {"suffix", required_argument
, NULL
, 'S'},
135 {"target-directory", required_argument
, NULL
, 't'},
136 {"verbose", no_argument
, NULL
, 'v'},
137 {GETOPT_HELP_OPTION_DECL
},
138 {GETOPT_VERSION_OPTION_DECL
},
142 /* Compare content of opened files using file descriptors A_FD and B_FD. Return
143 true if files are equal. */
145 have_same_content (int a_fd
, int b_fd
)
147 enum { CMP_BLOCK_SIZE
= 4096 };
148 static char a_buff
[CMP_BLOCK_SIZE
];
149 static char b_buff
[CMP_BLOCK_SIZE
];
152 while (0 < (size
= full_read (a_fd
, a_buff
, sizeof a_buff
))) {
153 if (size
!= full_read (b_fd
, b_buff
, sizeof b_buff
))
156 if (memcmp (a_buff
, b_buff
, size
) != 0)
163 /* Return true for mode with non-permission bits. */
165 extra_mode (mode_t input
)
167 mode_t mask
= S_IRWXUGO
| S_IFMT
;
168 return !! (input
& ~ mask
);
171 /* Return true if copy of file SRC_NAME to file DEST_NAME is necessary. */
173 need_copy (const char *src_name
, const char *dest_name
,
174 const struct cp_options
*x
)
176 struct stat src_sb
, dest_sb
;
180 if (extra_mode (mode
))
183 /* compare files using stat */
184 if (lstat (src_name
, &src_sb
) != 0)
187 if (lstat (dest_name
, &dest_sb
) != 0)
190 if (!S_ISREG (src_sb
.st_mode
) || !S_ISREG (dest_sb
.st_mode
)
191 || extra_mode (src_sb
.st_mode
) || extra_mode (dest_sb
.st_mode
))
194 if (src_sb
.st_size
!= dest_sb
.st_size
195 || (dest_sb
.st_mode
& CHMOD_MODE_BITS
) != mode
)
198 if (owner_id
== (uid_t
) -1)
201 uid_t ruid
= getuid ();
202 if ((ruid
== (uid_t
) -1 && errno
) || dest_sb
.st_uid
!= ruid
)
205 else if (dest_sb
.st_uid
!= owner_id
)
208 if (group_id
== (uid_t
) -1)
211 gid_t rgid
= getgid ();
212 if ((rgid
== (uid_t
) -1 && errno
) || dest_sb
.st_gid
!= rgid
)
215 else if (dest_sb
.st_gid
!= group_id
)
218 /* compare SELinux context if preserving */
219 if (selinux_enabled
&& x
->preserve_security_context
)
221 security_context_t file_scontext
= NULL
;
222 security_context_t to_scontext
= NULL
;
225 if (getfilecon (src_name
, &file_scontext
) == -1)
228 if (getfilecon (dest_name
, &to_scontext
) == -1)
230 freecon (file_scontext
);
234 scontext_match
= STREQ (file_scontext
, to_scontext
);
236 freecon (file_scontext
);
237 freecon (to_scontext
);
242 /* compare files content */
243 src_fd
= open (src_name
, O_RDONLY
| O_BINARY
);
247 dest_fd
= open (dest_name
, O_RDONLY
| O_BINARY
);
254 content_match
= have_same_content (src_fd
, dest_fd
);
258 return !content_match
;
262 cp_option_init (struct cp_options
*x
)
264 cp_options_default (x
);
265 x
->copy_as_regular
= true;
266 x
->reflink_mode
= REFLINK_NEVER
;
267 x
->dereference
= DEREF_ALWAYS
;
268 x
->unlink_dest_before_opening
= true;
269 x
->unlink_dest_after_failed_open
= false;
270 x
->hard_link
= false;
271 x
->interactive
= I_UNSPECIFIED
;
272 x
->move_mode
= false;
273 x
->one_file_system
= false;
274 x
->preserve_ownership
= false;
275 x
->preserve_links
= false;
276 x
->preserve_mode
= false;
277 x
->preserve_timestamps
= false;
278 x
->reduce_diagnostics
=false;
279 x
->data_copy_required
= true;
280 x
->require_preserve
= false;
281 x
->require_preserve_context
= false;
282 x
->require_preserve_xattr
= false;
283 x
->recursive
= false;
284 x
->sparse_mode
= SPARSE_AUTO
;
285 x
->symbolic_link
= false;
286 x
->backup_type
= no_backups
;
288 /* Create destination files initially writable so we can run strip on them.
289 Although GNU strip works fine on read-only files, some others
292 x
->mode
= S_IRUSR
| S_IWUSR
;
293 x
->stdin_tty
= false;
295 x
->open_dangling_dest_symlink
= false;
297 x
->preserve_security_context
= false;
298 x
->preserve_xattr
= false;
304 #ifdef ENABLE_MATCHPATHCON
305 /* Modify file context to match the specified policy.
306 If an error occurs the file will remain with the default directory
309 setdefaultfilecon (char const *file
)
312 security_context_t scontext
= NULL
;
313 static bool first_call
= true;
315 if (selinux_enabled
!= 1)
317 /* Indicate no context found. */
320 if (lstat (file
, &st
) != 0)
323 if (first_call
&& IS_ABSOLUTE_FILE_NAME (file
))
325 /* Calling matchpathcon_init_prefix (NULL, "/first_component/")
326 is an optimization to minimize the expense of the following
327 matchpathcon call. Do it only once, just before the first
328 matchpathcon call. We *could* call matchpathcon_fini after
329 the final matchpathcon call, but that's not necessary, since
330 by then we're about to exit, and besides, the buffers it
331 would free are still reachable. */
333 char const *p
= file
+ 1;
337 /* Record final leading slash, for when FILE starts with two or more. */
347 while (*p
&& !ISSLASH (*p
));
349 prefix
= malloc (p
- p0
+ 2);
352 stpcpy (stpncpy (prefix
, p0
, p
- p0
), "/");
353 matchpathcon_init_prefix (NULL
, prefix
);
360 /* If there's an error determining the context, or it has none,
361 return to allow default context */
362 if ((matchpathcon (file
, st
.st_mode
, &scontext
) != 0) ||
363 STREQ (scontext
, "<<none>>"))
365 if (scontext
!= NULL
)
370 if (lsetfilecon (file
, scontext
) < 0 && errno
!= ENOTSUP
)
372 _("warning: %s: failed to change context to %s"),
373 quotearg_colon (file
), scontext
);
380 setdefaultfilecon (char const *file
)
386 /* FILE is the last operand of this command. Return true if FILE is a
387 directory. But report an error there is a problem accessing FILE,
388 or if FILE does not exist but would have to refer to an existing
389 directory if it referred to anything at all. */
392 target_directory_operand (char const *file
)
394 char const *b
= last_component (file
);
395 size_t blen
= strlen (b
);
396 bool looks_like_a_dir
= (blen
== 0 || ISSLASH (b
[blen
- 1]));
398 int err
= (stat (file
, &st
) == 0 ? 0 : errno
);
399 bool is_a_dir
= !err
&& S_ISDIR (st
.st_mode
);
400 if (err
&& err
!= ENOENT
)
401 error (EXIT_FAILURE
, err
, _("accessing %s"), quote (file
));
402 if (is_a_dir
< looks_like_a_dir
)
403 error (EXIT_FAILURE
, err
, _("target %s is not a directory"), quote (file
));
407 /* Report that directory DIR was made, if OPTIONS requests this. */
409 announce_mkdir (char const *dir
, void *options
)
411 struct cp_options
const *x
= options
;
413 prog_fprintf (stdout
, _("creating directory %s"), quote (dir
));
416 /* Make ancestor directory DIR, whose last file name component is
417 COMPONENT, with options OPTIONS. Assume the working directory is
418 COMPONENT's parent. */
420 make_ancestor (char const *dir
, char const *component
, void *options
)
422 int r
= mkdir (component
, DEFAULT_MODE
);
424 announce_mkdir (dir
, options
);
428 /* Process a command-line file name, for the -d option. */
430 process_dir (char *dir
, struct savewd
*wd
, void *options
)
432 return (make_dir_parents (dir
, wd
,
433 make_ancestor
, options
,
434 dir_mode
, announce_mkdir
,
435 dir_mode_bits
, owner_id
, group_id
, false)
440 /* Copy file FROM onto file TO, creating TO if necessary.
441 Return true if successful. */
444 copy_file (const char *from
, const char *to
, const struct cp_options
*x
)
448 if (copy_only_if_needed
&& !need_copy (from
, to
, x
))
451 /* Allow installing from non-regular files like /dev/null.
452 Charles Karney reported that some Sun version of install allows that
453 and that sendmail's installation process relies on the behavior.
454 However, since !x->recursive, the call to "copy" will fail if FROM
457 return copy (from
, to
, false, x
, ©_into_self
, NULL
);
460 /* Set the attributes of file or directory NAME.
461 Return true if successful. */
464 change_attributes (char const *name
)
467 /* chown must precede chmod because on some systems,
468 chown clears the set[ug]id bits for non-superusers,
469 resulting in incorrect permissions.
470 On System V, users can give away files with chown and then not
471 be able to chmod them. So don't give files away.
473 We don't normally ignore errors from chown because the idea of
474 the install command is that the file is supposed to end up with
475 precisely the attributes that the user specified (or defaulted).
476 If the file doesn't end up with the group they asked for, they'll
479 if (! (owner_id
== (uid_t
) -1 && group_id
== (gid_t
) -1)
480 && lchown (name
, owner_id
, group_id
) != 0)
481 error (0, errno
, _("cannot change ownership of %s"), quote (name
));
482 else if (chmod (name
, mode
) != 0)
483 error (0, errno
, _("cannot change permissions of %s"), quote (name
));
487 if (use_default_selinux_context
)
488 setdefaultfilecon (name
);
493 /* Set the timestamps of file DEST to match those of SRC_SB.
494 Return true if successful. */
497 change_timestamps (struct stat
const *src_sb
, char const *dest
)
499 struct timespec timespec
[2];
500 timespec
[0] = get_stat_atime (src_sb
);
501 timespec
[1] = get_stat_mtime (src_sb
);
503 if (utimens (dest
, timespec
))
505 error (0, errno
, _("cannot set time stamps for %s"), quote (dest
));
511 /* Strip the symbol table from the file NAME.
512 We could dig the magic number out of the file first to
513 determine whether to strip it, but the header files and
514 magic numbers vary so much from system to system that making
515 it portable would be very difficult. Not worth the effort. */
518 strip (char const *name
)
526 error (EXIT_FAILURE
, errno
, _("fork system call failed"));
529 execlp (strip_program
, strip_program
, name
, NULL
);
530 error (EXIT_FAILURE
, errno
, _("cannot run %s"), strip_program
);
532 default: /* Parent. */
533 if (waitpid (pid
, &status
, 0) < 0)
534 error (EXIT_FAILURE
, errno
, _("waiting for strip"));
535 else if (! WIFEXITED (status
) || WEXITSTATUS (status
))
536 error (EXIT_FAILURE
, 0, _("strip process terminated abnormally"));
541 /* Initialize the user and group ownership of the files to install. */
551 pw
= getpwnam (owner_name
);
554 unsigned long int tmp
;
555 if (xstrtoul (owner_name
, NULL
, 0, &tmp
, NULL
) != LONGINT_OK
557 error (EXIT_FAILURE
, 0, _("invalid user %s"), quote (owner_name
));
561 owner_id
= pw
->pw_uid
;
565 owner_id
= (uid_t
) -1;
569 gr
= getgrnam (group_name
);
572 unsigned long int tmp
;
573 if (xstrtoul (group_name
, NULL
, 0, &tmp
, NULL
) != LONGINT_OK
575 error (EXIT_FAILURE
, 0, _("invalid group %s"), quote (group_name
));
579 group_id
= gr
->gr_gid
;
583 group_id
= (gid_t
) -1;
589 if (status
!= EXIT_SUCCESS
)
594 Usage: %s [OPTION]... [-T] SOURCE DEST\n\
595 or: %s [OPTION]... SOURCE... DIRECTORY\n\
596 or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
597 or: %s [OPTION]... -d DIRECTORY...\n\
599 program_name
, program_name
, program_name
, program_name
);
602 This install program copies files (often just compiled) into destination\n\
603 locations you choose. If you want to download and install a ready-to-use\n\
604 package on a GNU/Linux system, you should instead be using a package manager\n\
605 like yum(1) or apt-get(1).\n\
607 In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\
608 the existing DIRECTORY, while setting permission modes and owner/group.\n\
609 In the 4th form, create all components of the given DIRECTORY(ies).\n\
613 Mandatory arguments to long options are mandatory for short options too.\n\
616 --backup[=CONTROL] make a backup of each existing destination file\n\
617 -b like --backup but does not accept an argument\n\
619 -C, --compare compare each pair of source and destination files, and\n\
620 in some cases, do not modify the destination at all\n\
621 -d, --directory treat all arguments as directory names; create all\n\
622 components of the specified directories\n\
625 -D create all leading components of DEST except the last,\n\
626 then copy SOURCE to DEST\n\
627 -g, --group=GROUP set group ownership, instead of process' current group\n\
628 -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
629 -o, --owner=OWNER set ownership (super-user only)\n\
632 -p, --preserve-timestamps apply access/modification times of SOURCE files\n\
633 to corresponding destination files\n\
634 -s, --strip strip symbol tables\n\
635 --strip-program=PROGRAM program used to strip binaries\n\
636 -S, --suffix=SUFFIX override the usual backup suffix\n\
637 -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
638 -T, --no-target-directory treat DEST as a normal file\n\
639 -v, --verbose print the name of each directory as it is created\n\
642 --preserve-context preserve SELinux security context\n\
643 -Z, --context=CONTEXT set SELinux security context of files and directories\
647 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
648 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
651 The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
652 The version control method may be selected via the --backup option or through\n\
653 the VERSION_CONTROL environment variable. Here are the values:\n\
657 none, off never make backups (even if --backup is given)\n\
658 numbered, t make numbered backups\n\
659 existing, nil numbered if numbered backups exist, simple otherwise\n\
660 simple, never always make simple backups\n\
662 emit_ancillary_info ();
667 /* Copy file FROM onto file TO and give TO the appropriate
669 Return true if successful. */
672 install_file_in_file (const char *from
, const char *to
,
673 const struct cp_options
*x
)
676 if (x
->preserve_timestamps
&& stat (from
, &from_sb
) != 0)
678 error (0, errno
, _("cannot stat %s"), quote (from
));
681 if (! copy_file (from
, to
, x
))
685 if (x
->preserve_timestamps
&& (strip_files
|| ! S_ISREG (from_sb
.st_mode
))
686 && ! change_timestamps (&from_sb
, to
))
688 return change_attributes (to
);
691 /* Copy file FROM onto file TO, creating any missing parent directories of TO.
692 Return true if successful. */
695 install_file_in_file_parents (char const *from
, char *to
,
696 struct cp_options
*x
)
698 bool save_working_directory
=
699 ! (IS_ABSOLUTE_FILE_NAME (from
) && IS_ABSOLUTE_FILE_NAME (to
));
700 int status
= EXIT_SUCCESS
;
704 if (! save_working_directory
)
707 if (mkancesdirs (to
, &wd
, make_ancestor
, x
) == -1)
709 error (0, errno
, _("cannot create directory %s"), to
);
710 status
= EXIT_FAILURE
;
713 if (save_working_directory
)
715 int restore_result
= savewd_restore (&wd
, status
);
716 int restore_errno
= errno
;
718 if (EXIT_SUCCESS
< restore_result
)
720 if (restore_result
< 0 && status
== EXIT_SUCCESS
)
722 error (0, restore_errno
, _("cannot create directory %s"), to
);
727 return (status
== EXIT_SUCCESS
&& install_file_in_file (from
, to
, x
));
730 /* Copy file FROM into directory TO_DIR, keeping its same name,
731 and give the copy the appropriate attributes.
732 Return true if successful. */
735 install_file_in_dir (const char *from
, const char *to_dir
,
736 const struct cp_options
*x
)
738 const char *from_base
= last_component (from
);
739 char *to
= file_name_concat (to_dir
, from_base
, NULL
);
740 bool ret
= install_file_in_file (from
, to
, x
);
746 main (int argc
, char **argv
)
749 int exit_status
= EXIT_SUCCESS
;
750 const char *specified_mode
= NULL
;
751 bool make_backups
= false;
752 char *backup_suffix_string
;
753 char *version_control_string
= NULL
;
754 bool mkdir_and_install
= false;
756 char const *target_directory
= NULL
;
757 bool no_target_directory
= false;
760 bool strip_program_specified
= false;
761 security_context_t scontext
= NULL
;
762 /* set iff kernel has extra selinux system calls */
763 selinux_enabled
= (0 < is_selinux_enabled ());
765 initialize_main (&argc
, &argv
);
766 set_program_name (argv
[0]);
767 setlocale (LC_ALL
, "");
768 bindtextdomain (PACKAGE
, LOCALEDIR
);
769 textdomain (PACKAGE
);
771 atexit (close_stdin
);
781 /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
782 we'll actually use backup_suffix_string. */
783 backup_suffix_string
= getenv ("SIMPLE_BACKUP_SUFFIX");
785 while ((optc
= getopt_long (argc
, argv
, "bcCsDdg:m:o:pt:TvS:Z:", long_options
,
793 version_control_string
= optarg
;
798 copy_only_if_needed
= true;
803 /* System V fork+wait does not work if SIGCHLD is ignored. */
804 signal (SIGCHLD
, SIG_DFL
);
807 case STRIP_PROGRAM_OPTION
:
808 strip_program
= xstrdup (optarg
);
809 strip_program_specified
= true;
815 mkdir_and_install
= true;
824 specified_mode
= optarg
;
830 x
.preserve_timestamps
= true;
834 backup_suffix_string
= optarg
;
837 if (target_directory
)
838 error (EXIT_FAILURE
, 0,
839 _("multiple target directories specified"));
843 if (stat (optarg
, &st
) != 0)
844 error (EXIT_FAILURE
, errno
, _("accessing %s"), quote (optarg
));
845 if (! S_ISDIR (st
.st_mode
))
846 error (EXIT_FAILURE
, 0, _("target %s is not a directory"),
849 target_directory
= optarg
;
852 no_target_directory
= true;
855 case PRESERVE_CONTEXT_OPTION
:
856 if ( ! selinux_enabled
)
858 error (0, 0, _("WARNING: ignoring --preserve-context; "
859 "this kernel is not SELinux-enabled"));
862 x
.preserve_security_context
= true;
863 use_default_selinux_context
= false;
866 if ( ! selinux_enabled
)
868 error (0, 0, _("WARNING: ignoring --context (-Z); "
869 "this kernel is not SELinux-enabled"));
873 use_default_selinux_context
= false;
875 case_GETOPT_HELP_CHAR
;
876 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
878 usage (EXIT_FAILURE
);
882 /* Check for invalid combinations of arguments. */
883 if (dir_arg
&& strip_files
)
884 error (EXIT_FAILURE
, 0,
885 _("the strip option may not be used when installing a directory"));
886 if (dir_arg
&& target_directory
)
887 error (EXIT_FAILURE
, 0,
888 _("target directory not allowed when installing a directory"));
890 if (x
.preserve_security_context
&& scontext
!= NULL
)
891 error (EXIT_FAILURE
, 0,
892 _("cannot force target context to %s and preserve it"),
895 if (backup_suffix_string
)
896 simple_backup_suffix
= xstrdup (backup_suffix_string
);
898 x
.backup_type
= (make_backups
899 ? xget_version (_("backup type"),
900 version_control_string
)
903 if (scontext
&& setfscreatecon (scontext
) < 0)
904 error (EXIT_FAILURE
, errno
,
905 _("failed to set default file creation context to %s"),
908 n_files
= argc
- optind
;
909 file
= argv
+ optind
;
911 if (n_files
<= ! (dir_arg
|| target_directory
))
914 error (0, 0, _("missing file operand"));
916 error (0, 0, _("missing destination file operand after %s"),
918 usage (EXIT_FAILURE
);
921 if (no_target_directory
)
923 if (target_directory
)
924 error (EXIT_FAILURE
, 0,
925 _("cannot combine --target-directory (-t) "
926 "and --no-target-directory (-T)"));
929 error (0, 0, _("extra operand %s"), quote (file
[2]));
930 usage (EXIT_FAILURE
);
933 else if (! (dir_arg
|| target_directory
))
935 if (2 <= n_files
&& target_directory_operand (file
[n_files
- 1]))
936 target_directory
= file
[--n_files
];
937 else if (2 < n_files
)
938 error (EXIT_FAILURE
, 0, _("target %s is not a directory"),
939 quote (file
[n_files
- 1]));
944 struct mode_change
*change
= mode_compile (specified_mode
);
946 error (EXIT_FAILURE
, 0, _("invalid mode %s"), quote (specified_mode
));
947 mode
= mode_adjust (0, false, 0, change
, NULL
);
948 dir_mode
= mode_adjust (0, true, 0, change
, &dir_mode_bits
);
952 if (strip_program_specified
&& !strip_files
)
953 error (0, 0, _("WARNING: ignoring --strip-program option as -s option was "
956 if (copy_only_if_needed
&& x
.preserve_timestamps
)
958 error (0, 0, _("options --compare (-C) and --preserve-timestamps are "
959 "mutually exclusive"));
960 usage (EXIT_FAILURE
);
963 if (copy_only_if_needed
&& strip_files
)
965 error (0, 0, _("options --compare (-C) and --strip are mutually "
967 usage (EXIT_FAILURE
);
970 if (copy_only_if_needed
&& extra_mode (mode
))
971 error (0, 0, _("the --compare (-C) option is ignored when you"
972 " specify a mode with non-permission bits"));
977 exit_status
= savewd_process_files (n_files
, file
, process_dir
, &x
);
980 /* FIXME: it's a little gross that this initialization is
981 required by copy.c::copy. */
984 if (!target_directory
)
986 if (! (mkdir_and_install
987 ? install_file_in_file_parents (file
[0], file
[1], &x
)
988 : install_file_in_file (file
[0], file
[1], &x
)))
989 exit_status
= EXIT_FAILURE
;
995 for (i
= 0; i
< n_files
; i
++)
996 if (! install_file_in_dir (file
[i
], target_directory
, &x
))
997 exit_status
= EXIT_FAILURE
;