test: avoid false failure with setgid directories
[coreutils.git] / src / install.c
blobaccd0fd4e501a2d5148c87c46d360086dc0a2ad6
1 /* install - copy files and set attributes
2 Copyright (C) 1989-2024 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 <https://www.gnu.org/licenses/>. */
17 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <signal.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include <selinux/label.h>
27 #include <sys/wait.h>
29 #include "system.h"
30 #include "backupfile.h"
31 #include "cp-hash.h"
32 #include "copy.h"
33 #include "filenamecat.h"
34 #include "full-read.h"
35 #include "mkancesdirs.h"
36 #include "mkdir-p.h"
37 #include "modechange.h"
38 #include "prog-fprintf.h"
39 #include "quote.h"
40 #include "savewd.h"
41 #include "selinux.h"
42 #include "stat-time.h"
43 #include "targetdir.h"
44 #include "utimens.h"
45 #include "xstrtol.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;
55 #if ! HAVE_ENDGRENT
56 # define endgrent() ((void) 0)
57 #endif
59 #if ! HAVE_ENDPWENT
60 # define endpwent() ((void) 0)
61 #endif
63 /* The user name that will own the files, or nullptr to make the owner
64 the current user ID. */
65 static char *owner_name;
67 /* The user ID corresponding to 'owner_name'. */
68 static uid_t owner_id;
70 /* The group name that will own the files, or nullptr to make the group
71 the current group ID. */
72 static char *group_name;
74 /* The group ID corresponding to 'group_name'. */
75 static gid_t group_id;
77 #define DEFAULT_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
79 /* The file mode bits to which non-directory files will be set. The umask has
80 no effect. */
81 static mode_t mode = DEFAULT_MODE;
83 /* Similar, but for directories. */
84 static mode_t dir_mode = DEFAULT_MODE;
86 /* The file mode bits that the user cares about. This should be a
87 superset of DIR_MODE and a subset of CHMOD_MODE_BITS. This matters
88 for directories, since otherwise directories may keep their S_ISUID
89 or S_ISGID bits. */
90 static mode_t dir_mode_bits = CHMOD_MODE_BITS;
92 /* Compare files before installing (-C) */
93 static bool copy_only_if_needed;
95 /* If true, strip executable files after copying them. */
96 static bool strip_files;
98 /* If true, install a directory instead of a regular file. */
99 static bool dir_arg;
101 /* Program used to strip binaries, "strip" is default */
102 static char const *strip_program = "strip";
104 /* For long options that have no equivalent short option, use a
105 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
106 enum
108 DEBUG_OPTION = CHAR_MAX + 1,
109 PRESERVE_CONTEXT_OPTION,
110 STRIP_PROGRAM_OPTION
113 static struct option const long_options[] =
115 {"backup", optional_argument, nullptr, 'b'},
116 {"compare", no_argument, nullptr, 'C'},
117 {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
118 {"debug", no_argument, nullptr, DEBUG_OPTION},
119 {"directory", no_argument, nullptr, 'd'},
120 {"group", required_argument, nullptr, 'g'},
121 {"mode", required_argument, nullptr, 'm'},
122 {"no-target-directory", no_argument, nullptr, 'T'},
123 {"owner", required_argument, nullptr, 'o'},
124 {"preserve-timestamps", no_argument, nullptr, 'p'},
125 {"preserve-context", no_argument, nullptr, PRESERVE_CONTEXT_OPTION},
126 {"strip", no_argument, nullptr, 's'},
127 {"strip-program", required_argument, nullptr, STRIP_PROGRAM_OPTION},
128 {"suffix", required_argument, nullptr, 'S'},
129 {"target-directory", required_argument, nullptr, 't'},
130 {"verbose", no_argument, nullptr, 'v'},
131 {GETOPT_HELP_OPTION_DECL},
132 {GETOPT_VERSION_OPTION_DECL},
133 {nullptr, 0, nullptr, 0}
136 /* Compare content of opened files using file descriptors A_FD and B_FD. Return
137 true if files are equal. */
138 static bool
139 have_same_content (int a_fd, int b_fd)
141 enum { CMP_BLOCK_SIZE = 4096 };
142 static char a_buff[CMP_BLOCK_SIZE];
143 static char b_buff[CMP_BLOCK_SIZE];
145 size_t size;
146 while (0 < (size = full_read (a_fd, a_buff, sizeof a_buff))) {
147 if (size != full_read (b_fd, b_buff, sizeof b_buff))
148 return false;
150 if (memcmp (a_buff, b_buff, size) != 0)
151 return false;
154 return size == 0;
157 /* Return true for mode with non-permission bits. */
158 static bool
159 extra_mode (mode_t input)
161 mode_t mask = S_IRWXUGO | S_IFMT;
162 return !! (input & ~ mask);
165 /* Return true if copy of file SRC_NAME to file DEST_NAME aka
166 DEST_DIRFD+DEST_RELNAME is necessary. */
167 static bool
168 need_copy (char const *src_name, char const *dest_name,
169 int dest_dirfd, char const *dest_relname,
170 const struct cp_options *x)
172 struct stat src_sb, dest_sb;
173 int src_fd, dest_fd;
174 bool content_match;
176 if (extra_mode (mode))
177 return true;
179 /* compare files using stat */
180 if (lstat (src_name, &src_sb) != 0)
181 return true;
183 if (fstatat (dest_dirfd, dest_relname, &dest_sb, AT_SYMLINK_NOFOLLOW) != 0)
184 return true;
186 if (!S_ISREG (src_sb.st_mode) || !S_ISREG (dest_sb.st_mode)
187 || extra_mode (src_sb.st_mode) || extra_mode (dest_sb.st_mode))
188 return true;
190 if (src_sb.st_size != dest_sb.st_size
191 || (dest_sb.st_mode & CHMOD_MODE_BITS) != mode)
192 return true;
194 if (owner_id == (uid_t) -1)
196 errno = 0;
197 uid_t ruid = getuid ();
198 if ((ruid == (uid_t) -1 && errno) || dest_sb.st_uid != ruid)
199 return true;
201 else if (dest_sb.st_uid != owner_id)
202 return true;
204 if (group_id == (uid_t) -1)
206 errno = 0;
207 gid_t rgid = getgid ();
208 if ((rgid == (uid_t) -1 && errno) || dest_sb.st_gid != rgid)
209 return true;
211 else if (dest_sb.st_gid != group_id)
212 return true;
214 /* compare SELinux context if preserving */
215 if (selinux_enabled && x->preserve_security_context)
217 char *file_scontext_raw = nullptr;
218 char *to_scontext_raw = nullptr;
219 bool scontext_match;
221 if (getfilecon_raw (src_name, &file_scontext_raw) == -1)
222 return true;
224 if (getfilecon_raw (dest_name, &to_scontext_raw) == -1)
226 freecon (file_scontext_raw);
227 return true;
230 scontext_match = STREQ (file_scontext_raw, to_scontext_raw);
232 freecon (file_scontext_raw);
233 freecon (to_scontext_raw);
234 if (!scontext_match)
235 return true;
238 /* compare files content */
239 src_fd = open (src_name, O_RDONLY | O_BINARY);
240 if (src_fd < 0)
241 return true;
243 dest_fd = openat (dest_dirfd, dest_relname, O_RDONLY | O_BINARY);
244 if (dest_fd < 0)
246 close (src_fd);
247 return true;
250 content_match = have_same_content (src_fd, dest_fd);
252 close (src_fd);
253 close (dest_fd);
254 return !content_match;
257 static void
258 cp_option_init (struct cp_options *x)
260 cp_options_default (x);
261 x->copy_as_regular = true;
262 x->reflink_mode = REFLINK_AUTO;
263 x->dereference = DEREF_ALWAYS;
264 x->unlink_dest_before_opening = true;
265 x->unlink_dest_after_failed_open = false;
266 x->hard_link = false;
267 x->interactive = I_UNSPECIFIED;
268 x->move_mode = false;
269 x->install_mode = true;
270 x->one_file_system = false;
271 x->preserve_ownership = false;
272 x->preserve_links = false;
273 x->preserve_mode = false;
274 x->preserve_timestamps = false;
275 x->explicit_no_preserve_mode = false;
276 x->reduce_diagnostics=false;
277 x->data_copy_required = true;
278 x->require_preserve = false;
279 x->require_preserve_xattr = false;
280 x->recursive = false;
281 x->sparse_mode = SPARSE_AUTO;
282 x->symbolic_link = false;
283 x->backup_type = no_backups;
285 /* Create destination files initially writable so we can run strip on them.
286 Although GNU strip works fine on read-only files, some others
287 would fail. */
288 x->set_mode = true;
289 x->mode = S_IRUSR | S_IWUSR;
290 x->stdin_tty = false;
292 x->open_dangling_dest_symlink = false;
293 x->update = false;
294 x->require_preserve_context = false; /* Not used by install currently. */
295 x->preserve_security_context = false; /* Whether to copy context from src. */
296 x->set_security_context = nullptr; /* Whether to set sys default context. */
297 x->preserve_xattr = false;
298 x->verbose = false;
299 x->dest_info = nullptr;
300 x->src_info = nullptr;
303 static struct selabel_handle *
304 get_labeling_handle (void)
306 static bool initialized;
307 static struct selabel_handle *hnd;
308 if (!initialized)
310 initialized = true;
311 hnd = selabel_open (SELABEL_CTX_FILE, nullptr, 0);
312 if (!hnd)
313 error (0, errno, _("warning: security labeling handle failed"));
315 return hnd;
318 /* Modify file context to match the specified policy.
319 If an error occurs the file will remain with the default directory
320 context. Note this sets the context to that returned by selabel_lookup
321 and thus discards MLS levels and user identity of the FILE. */
322 static void
323 setdefaultfilecon (char const *file)
325 struct stat st;
326 char *scontext_raw = nullptr;
328 if (selinux_enabled != 1)
330 /* Indicate no context found. */
331 return;
333 if (lstat (file, &st) != 0)
334 return;
336 struct selabel_handle *hnd = get_labeling_handle ();
337 if (!hnd)
338 return;
339 if (selabel_lookup_raw (hnd, &scontext_raw, file, st.st_mode) != 0)
341 if (errno != ENOENT && ! ignorable_ctx_err (errno))
342 error (0, errno, _("warning: %s: context lookup failed"),
343 quotef (file));
344 return;
347 if (lsetfilecon_raw (file, scontext_raw) < 0 && errno != ENOTSUP)
348 error (0, errno,
349 _("warning: %s: failed to change context to %s"),
350 quotef_n (0, file), quote_n (1, scontext_raw));
352 freecon (scontext_raw);
355 /* Report that directory DIR was made, if OPTIONS requests this. */
356 static void
357 announce_mkdir (char const *dir, void *options)
359 struct cp_options const *x = options;
360 if (x->verbose)
361 prog_fprintf (stdout, _("creating directory %s"), quoteaf (dir));
364 /* Make ancestor directory DIR, whose last file name component is
365 COMPONENT, with options OPTIONS. Assume the working directory is
366 COMPONENT's parent. */
367 static int
368 make_ancestor (char const *dir, char const *component, void *options)
370 struct cp_options const *x = options;
371 if (x->set_security_context
372 && defaultcon (x->set_security_context, component, S_IFDIR) < 0
373 && ! ignorable_ctx_err (errno))
374 error (0, errno, _("failed to set default creation context for %s"),
375 quoteaf (dir));
377 int r = mkdir (component, DEFAULT_MODE);
378 if (r == 0)
379 announce_mkdir (dir, options);
380 return r;
383 /* Process a command-line file name, for the -d option. */
384 static int
385 process_dir (char *dir, struct savewd *wd, void *options)
387 struct cp_options const *x = options;
389 int ret = (make_dir_parents (dir, wd, make_ancestor, options,
390 dir_mode, announce_mkdir,
391 dir_mode_bits, owner_id, group_id, false)
392 ? EXIT_SUCCESS
393 : EXIT_FAILURE);
395 /* FIXME: Due to the current structure of make_dir_parents()
396 we don't have the facility to call defaultcon() before the
397 final component of DIR is created. So for now, create the
398 final component with the context from previous component
399 and here we set the context for the final component. */
400 if (ret == EXIT_SUCCESS && x->set_security_context)
402 if (! restorecon (x->set_security_context, last_component (dir), false)
403 && ! ignorable_ctx_err (errno))
404 error (0, errno, _("failed to restore context for %s"),
405 quoteaf (dir));
408 return ret;
411 /* Copy file FROM onto file TO aka TO_DIRFD+TO_RELNAME, creating TO if
412 necessary. Return true if successful. */
414 static bool
415 copy_file (char const *from, char const *to,
416 int to_dirfd, char const *to_relname, const struct cp_options *x)
418 bool copy_into_self;
420 if (copy_only_if_needed && !need_copy (from, to, to_dirfd, to_relname, x))
421 return true;
423 /* Allow installing from non-regular files like /dev/null.
424 Charles Karney reported that some Sun version of install allows that
425 and that sendmail's installation process relies on the behavior.
426 However, since !x->recursive, the call to "copy" will fail if FROM
427 is a directory. */
429 return copy (from, to, to_dirfd, to_relname, 0, x, &copy_into_self, nullptr);
432 /* Set the attributes of file or directory NAME aka DIRFD+RELNAME.
433 Return true if successful. */
435 static bool
436 change_attributes (char const *name, int dirfd, char const *relname)
438 bool ok = false;
439 /* chown must precede chmod because on some systems,
440 chown clears the set[ug]id bits for non-superusers,
441 resulting in incorrect permissions.
442 On System V, users can give away files with chown and then not
443 be able to chmod them. So don't give files away.
445 We don't normally ignore errors from chown because the idea of
446 the install command is that the file is supposed to end up with
447 precisely the attributes that the user specified (or defaulted).
448 If the file doesn't end up with the group they asked for, they'll
449 want to know. */
451 if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1)
452 && lchownat (dirfd, relname, owner_id, group_id) != 0)
453 error (0, errno, _("cannot change ownership of %s"), quoteaf (name));
454 else if (chmodat (dirfd, relname, mode) != 0)
455 error (0, errno, _("cannot change permissions of %s"), quoteaf (name));
456 else
457 ok = true;
459 if (use_default_selinux_context)
460 setdefaultfilecon (name);
462 return ok;
465 /* Set the timestamps of file DEST aka DIRFD+RELNAME to match those of SRC_SB.
466 Return true if successful. */
468 static bool
469 change_timestamps (struct stat const *src_sb, char const *dest,
470 int dirfd, char const *relname)
472 struct timespec timespec[2];
473 timespec[0] = get_stat_atime (src_sb);
474 timespec[1] = get_stat_mtime (src_sb);
476 if (utimensat (dirfd, relname, timespec, 0))
478 error (0, errno, _("cannot set timestamps for %s"), quoteaf (dest));
479 return false;
481 return true;
484 /* Strip the symbol table from the file NAME.
485 We could dig the magic number out of the file first to
486 determine whether to strip it, but the header files and
487 magic numbers vary so much from system to system that making
488 it portable would be very difficult. Not worth the effort. */
490 static bool
491 strip (char const *name)
493 int status;
494 bool ok = false;
495 pid_t pid = fork ();
497 switch (pid)
499 case -1:
500 error (0, errno, _("fork system call failed"));
501 break;
502 case 0: /* Child. */
504 char const *safe_name = name;
505 if (name && *name == '-')
506 safe_name = file_name_concat (".", name, nullptr);
507 execlp (strip_program, strip_program, safe_name, nullptr);
508 error (EXIT_FAILURE, errno, _("cannot run %s"),
509 quoteaf (strip_program));
511 default: /* Parent. */
512 if (waitpid (pid, &status, 0) < 0)
513 error (0, errno, _("waiting for strip"));
514 else if (! WIFEXITED (status) || WEXITSTATUS (status))
515 error (0, 0, _("strip process terminated abnormally"));
516 else
517 ok = true; /* strip succeeded */
518 break;
520 return ok;
523 /* Initialize the user and group ownership of the files to install. */
525 static void
526 get_ids (void)
528 struct passwd *pw;
529 struct group *gr;
531 if (owner_name)
533 pw = getpwnam (owner_name);
534 if (pw == nullptr)
536 uintmax_t tmp;
537 if (xstrtoumax (owner_name, nullptr, 0, &tmp, "") != LONGINT_OK
538 || UID_T_MAX < tmp)
539 error (EXIT_FAILURE, 0, _("invalid user %s"),
540 quoteaf (owner_name));
541 owner_id = tmp;
543 else
544 owner_id = pw->pw_uid;
545 endpwent ();
547 else
548 owner_id = (uid_t) -1;
550 if (group_name)
552 gr = getgrnam (group_name);
553 if (gr == nullptr)
555 uintmax_t tmp;
556 if (xstrtoumax (group_name, nullptr, 0, &tmp, "") != LONGINT_OK
557 || GID_T_MAX < tmp)
558 error (EXIT_FAILURE, 0, _("invalid group %s"),
559 quoteaf (group_name));
560 group_id = tmp;
562 else
563 group_id = gr->gr_gid;
564 endgrent ();
566 else
567 group_id = (gid_t) -1;
570 void
571 usage (int status)
573 if (status != EXIT_SUCCESS)
574 emit_try_help ();
575 else
577 printf (_("\
578 Usage: %s [OPTION]... [-T] SOURCE DEST\n\
579 or: %s [OPTION]... SOURCE... DIRECTORY\n\
580 or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
581 or: %s [OPTION]... -d DIRECTORY...\n\
583 program_name, program_name, program_name, program_name);
584 fputs (_("\
586 This install program copies files (often just compiled) into destination\n\
587 locations you choose. If you want to download and install a ready-to-use\n\
588 package on a GNU/Linux system, you should instead be using a package manager\n\
589 like yum(1) or apt-get(1).\n\
591 In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\
592 the existing DIRECTORY, while setting permission modes and owner/group.\n\
593 In the 4th form, create all components of the given DIRECTORY(ies).\n\
594 "), stdout);
596 emit_mandatory_arg_note ();
598 fputs (_("\
599 --backup[=CONTROL] make a backup of each existing destination file\n\
600 -b like --backup but does not accept an argument\n\
601 -c (ignored)\n\
602 -C, --compare compare content of source and destination files, and\n\
603 if no change to content, ownership, and permissions,\n\
604 do not modify the destination at all\n\
605 -d, --directory treat all arguments as directory names; create all\n\
606 components of the specified directories\n\
607 "), stdout);
608 fputs (_("\
609 -D create all leading components of DEST except the last,\n\
610 or all components of --target-directory,\n\
611 then copy SOURCE to DEST\n\
612 "), stdout);
613 fputs (_("\
614 --debug explain how a file is copied. Implies -v\n\
615 "), stdout);
616 fputs (_("\
617 -g, --group=GROUP set group ownership, instead of process' current group\n\
618 -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
619 -o, --owner=OWNER set ownership (super-user only)\n\
620 "), stdout);
621 fputs (_("\
622 -p, --preserve-timestamps apply access/modification times of SOURCE files\n\
623 to corresponding destination files\n\
624 -s, --strip strip symbol tables\n\
625 --strip-program=PROGRAM program used to strip binaries\n\
626 -S, --suffix=SUFFIX override the usual backup suffix\n\
627 -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
628 -T, --no-target-directory treat DEST as a normal file\n\
629 "), stdout);
630 fputs (_("\
631 -v, --verbose print the name of each created file or directory\n\
632 "), stdout);
633 fputs (_("\
634 --preserve-context preserve SELinux security context\n\
635 -Z set SELinux security context of destination\n\
636 file and each created directory to default type\n\
637 --context[=CTX] like -Z, or if CTX is specified then set the\n\
638 SELinux or SMACK security context to CTX\n\
639 "), stdout);
641 fputs (HELP_OPTION_DESCRIPTION, stdout);
642 fputs (VERSION_OPTION_DESCRIPTION, stdout);
643 emit_backup_suffix_note ();
644 emit_ancillary_info (PROGRAM_NAME);
646 exit (status);
649 /* Copy file FROM onto file TO aka TO_DIRFD+TO_RELNAME and give TO the
650 appropriate attributes. X gives the command options.
651 Return true if successful. */
653 static bool
654 install_file_in_file (char const *from, char const *to,
655 int to_dirfd, char const *to_relname,
656 const struct cp_options *x)
658 struct stat from_sb;
659 if (x->preserve_timestamps && stat (from, &from_sb) != 0)
661 error (0, errno, _("cannot stat %s"), quoteaf (from));
662 return false;
664 if (! copy_file (from, to, to_dirfd, to_relname, x))
665 return false;
666 if (strip_files)
667 if (! strip (to))
669 if (unlinkat (to_dirfd, to_relname, 0) != 0) /* Cleanup. */
670 error (EXIT_FAILURE, errno, _("cannot unlink %s"), quoteaf (to));
671 return false;
673 if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode))
674 && ! change_timestamps (&from_sb, to, to_dirfd, to_relname))
675 return false;
676 return change_attributes (to, to_dirfd, to_relname);
679 /* Create any missing parent directories of TO,
680 while maintaining the current Working Directory.
681 Return true if successful. */
683 static bool
684 mkancesdirs_safe_wd (char const *from, char *to, struct cp_options *x,
685 bool save_always)
687 bool save_working_directory =
688 save_always
689 || ! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
690 int status = EXIT_SUCCESS;
692 struct savewd wd;
693 savewd_init (&wd);
694 if (! save_working_directory)
695 savewd_finish (&wd);
697 if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
699 error (0, errno, _("cannot create directory %s"), quoteaf (to));
700 status = EXIT_FAILURE;
703 if (save_working_directory)
705 int restore_result = savewd_restore (&wd, status);
706 int restore_errno = errno;
707 savewd_finish (&wd);
708 if (EXIT_SUCCESS < restore_result)
709 return false;
710 if (restore_result < 0 && status == EXIT_SUCCESS)
712 error (0, restore_errno, _("cannot create directory %s"),
713 quoteaf (to));
714 return false;
717 return status == EXIT_SUCCESS;
720 /* Copy file FROM onto file TO, creating any missing parent directories of TO.
721 Return true if successful. */
723 static bool
724 install_file_in_file_parents (char const *from, char *to,
725 const struct cp_options *x)
727 return (mkancesdirs_safe_wd (from, to, (struct cp_options *)x, false)
728 && install_file_in_file (from, to, AT_FDCWD, to, x));
731 /* Copy file FROM into directory TO_DIR, keeping its same name,
732 and give the copy the appropriate attributes.
733 Return true if successful. */
735 static bool
736 install_file_in_dir (char const *from, char const *to_dir,
737 const struct cp_options *x, bool mkdir_and_install,
738 int *target_dirfd)
740 char const *from_base = last_component (from);
741 char *to_relname;
742 char *to = file_name_concat (to_dir, from_base, &to_relname);
743 bool ret = true;
745 if (!target_dirfd_valid (*target_dirfd)
746 && (ret = mkdir_and_install)
747 && (ret = mkancesdirs_safe_wd (from, to, (struct cp_options *) x, true)))
749 int fd = open (to_dir, O_PATHSEARCH | O_DIRECTORY);
750 if (fd < 0)
752 error (0, errno, _("cannot open %s"), quoteaf (to));
753 ret = false;
755 else
756 *target_dirfd = fd;
759 if (ret)
761 int to_dirfd = *target_dirfd;
762 if (!target_dirfd_valid (to_dirfd))
764 to_dirfd = AT_FDCWD;
765 to_relname = to;
767 ret = install_file_in_file (from, to, to_dirfd, to_relname, x);
770 free (to);
771 return ret;
775 main (int argc, char **argv)
777 int optc;
778 int exit_status = EXIT_SUCCESS;
779 char const *specified_mode = nullptr;
780 bool make_backups = false;
781 char const *backup_suffix = nullptr;
782 char *version_control_string = nullptr;
783 bool mkdir_and_install = false;
784 struct cp_options x;
785 char const *target_directory = nullptr;
786 bool no_target_directory = false;
787 int n_files;
788 char **file;
789 bool strip_program_specified = false;
790 char const *scontext = nullptr;
791 /* set iff kernel has extra selinux system calls */
792 selinux_enabled = (0 < is_selinux_enabled ());
794 initialize_main (&argc, &argv);
795 set_program_name (argv[0]);
796 setlocale (LC_ALL, "");
797 bindtextdomain (PACKAGE, LOCALEDIR);
798 textdomain (PACKAGE);
800 atexit (close_stdin);
802 cp_option_init (&x);
804 owner_name = nullptr;
805 group_name = nullptr;
806 strip_files = false;
807 dir_arg = false;
808 umask (0);
810 while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options,
811 nullptr))
812 != -1)
814 switch (optc)
816 case 'b':
817 make_backups = true;
818 if (optarg)
819 version_control_string = optarg;
820 break;
821 case 'c':
822 break;
823 case 'C':
824 copy_only_if_needed = true;
825 break;
826 case 's':
827 strip_files = true;
828 #ifdef SIGCHLD
829 /* System V fork+wait does not work if SIGCHLD is ignored. */
830 signal (SIGCHLD, SIG_DFL);
831 #endif
832 break;
833 case DEBUG_OPTION:
834 x.debug = x.verbose = true;
835 break;
836 case STRIP_PROGRAM_OPTION:
837 strip_program = xstrdup (optarg);
838 strip_program_specified = true;
839 break;
840 case 'd':
841 dir_arg = true;
842 break;
843 case 'D':
844 mkdir_and_install = true;
845 break;
846 case 'v':
847 x.verbose = true;
848 break;
849 case 'g':
850 group_name = optarg;
851 break;
852 case 'm':
853 specified_mode = optarg;
854 break;
855 case 'o':
856 owner_name = optarg;
857 break;
858 case 'p':
859 x.preserve_timestamps = true;
860 break;
861 case 'S':
862 make_backups = true;
863 backup_suffix = optarg;
864 break;
865 case 't':
866 if (target_directory)
867 error (EXIT_FAILURE, 0,
868 _("multiple target directories specified"));
869 target_directory = optarg;
870 break;
871 case 'T':
872 no_target_directory = true;
873 break;
875 case PRESERVE_CONTEXT_OPTION:
876 if (! selinux_enabled)
878 error (0, 0, _("WARNING: ignoring --preserve-context; "
879 "this kernel is not SELinux-enabled"));
880 break;
882 x.preserve_security_context = true;
883 use_default_selinux_context = false;
884 break;
885 case 'Z':
886 if (selinux_enabled)
888 /* Disable use of the install(1) specific setdefaultfilecon().
889 Note setdefaultfilecon() is different from the newer and more
890 generic restorecon() in that the former sets the context of
891 the dest files to that returned by selabel_lookup directly,
892 thus discarding MLS level and user identity of the file.
893 TODO: consider removing setdefaultfilecon() in future. */
894 use_default_selinux_context = false;
896 if (optarg)
897 scontext = optarg;
898 else
899 x.set_security_context = get_labeling_handle ();
901 else if (optarg)
903 error (0, 0,
904 _("warning: ignoring --context; "
905 "it requires an SELinux-enabled kernel"));
907 break;
908 case_GETOPT_HELP_CHAR;
909 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
910 default:
911 usage (EXIT_FAILURE);
915 /* Check for invalid combinations of arguments. */
916 if (dir_arg && strip_files)
917 error (EXIT_FAILURE, 0,
918 _("the strip option may not be used when installing a directory"));
919 if (dir_arg && target_directory)
920 error (EXIT_FAILURE, 0,
921 _("target directory not allowed when installing a directory"));
923 x.backup_type = (make_backups
924 ? xget_version (_("backup type"),
925 version_control_string)
926 : no_backups);
927 set_simple_backup_suffix (backup_suffix);
929 if (x.preserve_security_context && (x.set_security_context || scontext))
930 error (EXIT_FAILURE, 0,
931 _("cannot set target context and preserve it"));
933 if (scontext && setfscreatecon (scontext) < 0)
934 error (EXIT_FAILURE, errno,
935 _("failed to set default file creation context to %s"),
936 quote (scontext));
938 n_files = argc - optind;
939 file = argv + optind;
941 if (n_files <= ! (dir_arg || target_directory))
943 if (n_files <= 0)
944 error (0, 0, _("missing file operand"));
945 else
946 error (0, 0, _("missing destination file operand after %s"),
947 quoteaf (file[0]));
948 usage (EXIT_FAILURE);
951 struct stat sb;
952 int target_dirfd = AT_FDCWD;
953 if (no_target_directory)
955 if (target_directory)
956 error (EXIT_FAILURE, 0,
957 _("cannot combine --target-directory (-t) "
958 "and --no-target-directory (-T)"));
959 if (2 < n_files)
961 error (0, 0, _("extra operand %s"), quoteaf (file[2]));
962 usage (EXIT_FAILURE);
965 else if (target_directory)
967 target_dirfd = target_directory_operand (target_directory, &sb);
968 if (! (target_dirfd_valid (target_dirfd)
969 || (mkdir_and_install && errno == ENOENT)))
970 error (EXIT_FAILURE, errno, _("failed to access %s"),
971 quoteaf (target_directory));
973 else if (!dir_arg)
975 char const *lastfile = file[n_files - 1];
976 int fd = target_directory_operand (lastfile, &sb);
977 if (target_dirfd_valid (fd))
979 target_dirfd = fd;
980 target_directory = lastfile;
981 n_files--;
983 else if (2 < n_files)
984 error (EXIT_FAILURE, errno, _("target %s"), quoteaf (lastfile));
987 if (specified_mode)
989 struct mode_change *change = mode_compile (specified_mode);
990 if (!change)
991 error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode));
992 mode = mode_adjust (0, false, 0, change, nullptr);
993 dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits);
994 free (change);
997 if (strip_program_specified && !strip_files)
998 error (0, 0, _("WARNING: ignoring --strip-program option as -s option was "
999 "not specified"));
1001 if (copy_only_if_needed && x.preserve_timestamps)
1003 error (0, 0, _("options --compare (-C) and --preserve-timestamps are "
1004 "mutually exclusive"));
1005 usage (EXIT_FAILURE);
1008 if (copy_only_if_needed && strip_files)
1010 error (0, 0, _("options --compare (-C) and --strip are mutually "
1011 "exclusive"));
1012 usage (EXIT_FAILURE);
1015 if (copy_only_if_needed && extra_mode (mode))
1016 error (0, 0, _("the --compare (-C) option is ignored when you"
1017 " specify a mode with non-permission bits"));
1019 get_ids ();
1021 if (dir_arg)
1022 exit_status = savewd_process_files (n_files, file, process_dir, &x);
1023 else
1025 /* FIXME: it's a little gross that this initialization is
1026 required by copy.c::copy. */
1027 hash_init ();
1029 if (!target_directory)
1031 if (! (mkdir_and_install
1032 ? install_file_in_file_parents (file[0], file[1], &x)
1033 : install_file_in_file (file[0], file[1], AT_FDCWD,
1034 file[1], &x)))
1035 exit_status = EXIT_FAILURE;
1037 else
1039 int i;
1040 dest_info_init (&x);
1041 for (i = 0; i < n_files; i++)
1042 if (! install_file_in_dir (file[i], target_directory, &x,
1043 i == 0 && mkdir_and_install,
1044 &target_dirfd))
1045 exit_status = EXIT_FAILURE;
1049 main_exit (exit_status);