1 /* install - copy files and set attributes
2    Copyright (C) 1989-2023 Free Software Foundation, Inc.
3 
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.
8 
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.
13 
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/>.  */
16 
17 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
18 
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>
28 
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"
46 
47 /* The official name of this program (e.g., no 'g' prefix).  */
48 #define PROGRAM_NAME "install"
49 
50 #define AUTHORS proper_name ("David MacKenzie")
51 
52 static int selinux_enabled = 0;
53 static bool use_default_selinux_context = true;
54 
55 #if ! HAVE_ENDGRENT
56 # define endgrent() ((void) 0)
57 #endif
58 
59 #if ! HAVE_ENDPWENT
60 # define endpwent() ((void) 0)
61 #endif
62 
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;
66 
67 /* The user ID corresponding to 'owner_name'. */
68 static uid_t owner_id;
69 
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;
73 
74 /* The group ID corresponding to 'group_name'. */
75 static gid_t group_id;
76 
77 #define DEFAULT_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
78 
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;
82 
83 /* Similar, but for directories.  */
84 static mode_t dir_mode = DEFAULT_MODE;
85 
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;
91 
92 /* Compare files before installing (-C) */
93 static bool copy_only_if_needed;
94 
95 /* If true, strip executable files after copying them. */
96 static bool strip_files;
97 
98 /* If true, install a directory instead of a regular file. */
99 static bool dir_arg;
100 
101 /* Program used to strip binaries, "strip" is default */
102 static char const *strip_program = "strip";
103 
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
107 {
108   DEBUG_OPTION = CHAR_MAX + 1,
109   PRESERVE_CONTEXT_OPTION,
110   STRIP_PROGRAM_OPTION
111 };
112 
113 static struct option const long_options[] =
114 {
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}
134 };
135 
136 /* Compare content of opened files using file descriptors A_FD and B_FD. Return
137    true if files are equal. */
138 static bool
have_same_content(int a_fd,int b_fd)139 have_same_content (int a_fd, int b_fd)
140 {
141   enum { CMP_BLOCK_SIZE = 4096 };
142   static char a_buff[CMP_BLOCK_SIZE];
143   static char b_buff[CMP_BLOCK_SIZE];
144 
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;
149 
150     if (memcmp (a_buff, b_buff, size) != 0)
151       return false;
152   }
153 
154   return size == 0;
155 }
156 
157 /* Return true for mode with non-permission bits. */
158 static bool
extra_mode(mode_t input)159 extra_mode (mode_t input)
160 {
161   mode_t mask = S_IRWXUGO | S_IFMT;
162   return !! (input & ~ mask);
163 }
164 
165 /* Return true if copy of file SRC_NAME to file DEST_NAME aka
166    DEST_DIRFD+DEST_RELNAME is necessary. */
167 static bool
need_copy(char const * src_name,char const * dest_name,int dest_dirfd,char const * dest_relname,const struct cp_options * x)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)
171 {
172   struct stat src_sb, dest_sb;
173   int src_fd, dest_fd;
174   bool content_match;
175 
176   if (extra_mode (mode))
177     return true;
178 
179   /* compare files using stat */
180   if (lstat (src_name, &src_sb) != 0)
181     return true;
182 
183   if (fstatat (dest_dirfd, dest_relname, &dest_sb, AT_SYMLINK_NOFOLLOW) != 0)
184     return true;
185 
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;
189 
190   if (src_sb.st_size != dest_sb.st_size
191       || (dest_sb.st_mode & CHMOD_MODE_BITS) != mode)
192     return true;
193 
194   if (owner_id == (uid_t) -1)
195     {
196       errno = 0;
197       uid_t ruid = getuid ();
198       if ((ruid == (uid_t) -1 && errno) || dest_sb.st_uid != ruid)
199         return true;
200     }
201   else if (dest_sb.st_uid != owner_id)
202     return true;
203 
204   if (group_id == (uid_t) -1)
205     {
206       errno = 0;
207       gid_t rgid = getgid ();
208       if ((rgid == (uid_t) -1 && errno) || dest_sb.st_gid != rgid)
209         return true;
210     }
211   else if (dest_sb.st_gid != group_id)
212     return true;
213 
214   /* compare SELinux context if preserving */
215   if (selinux_enabled && x->preserve_security_context)
216     {
217       char *file_scontext = nullptr;
218       char *to_scontext = nullptr;
219       bool scontext_match;
220 
221       if (getfilecon (src_name, &file_scontext) == -1)
222         return true;
223 
224       if (getfilecon (dest_name, &to_scontext) == -1)
225         {
226           freecon (file_scontext);
227           return true;
228         }
229 
230       scontext_match = STREQ (file_scontext, to_scontext);
231 
232       freecon (file_scontext);
233       freecon (to_scontext);
234       if (!scontext_match)
235         return true;
236     }
237 
238   /* compare files content */
239   src_fd = open (src_name, O_RDONLY | O_BINARY);
240   if (src_fd < 0)
241     return true;
242 
243   dest_fd = openat (dest_dirfd, dest_relname, O_RDONLY | O_BINARY);
244   if (dest_fd < 0)
245     {
246       close (src_fd);
247       return true;
248     }
249 
250   content_match = have_same_content (src_fd, dest_fd);
251 
252   close (src_fd);
253   close (dest_fd);
254   return !content_match;
255 }
256 
257 static void
cp_option_init(struct cp_options * x)258 cp_option_init (struct cp_options *x)
259 {
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;
284 
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;
291 
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;
301 }
302 
303 static struct selabel_handle *
get_labeling_handle(void)304 get_labeling_handle (void)
305 {
306   static bool initialized;
307   static struct selabel_handle *hnd;
308   if (!initialized)
309     {
310       initialized = true;
311       hnd = selabel_open (SELABEL_CTX_FILE, nullptr, 0);
312       if (!hnd)
313         error (0, errno, _("warning: security labeling handle failed"));
314     }
315   return hnd;
316 }
317 
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
setdefaultfilecon(char const * file)323 setdefaultfilecon (char const *file)
324 {
325   struct stat st;
326   char *scontext = nullptr;
327 
328   if (selinux_enabled != 1)
329     {
330       /* Indicate no context found. */
331       return;
332     }
333   if (lstat (file, &st) != 0)
334     return;
335 
336   struct selabel_handle *hnd = get_labeling_handle ();
337   if (!hnd)
338     return;
339   if (selabel_lookup (hnd, &scontext, file, st.st_mode) != 0)
340     {
341       if (errno != ENOENT && ! ignorable_ctx_err (errno))
342         error (0, errno, _("warning: %s: context lookup failed"),
343                quotef (file));
344       return;
345     }
346 
347   if (lsetfilecon (file, scontext) < 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));
351 
352   freecon (scontext);
353 }
354 
355 /* Report that directory DIR was made, if OPTIONS requests this.  */
356 static void
announce_mkdir(char const * dir,void * options)357 announce_mkdir (char const *dir, void *options)
358 {
359   struct cp_options const *x = options;
360   if (x->verbose)
361     prog_fprintf (stdout, _("creating directory %s"), quoteaf (dir));
362 }
363 
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
make_ancestor(char const * dir,char const * component,void * options)368 make_ancestor (char const *dir, char const *component, void *options)
369 {
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));
376 
377   int r = mkdir (component, DEFAULT_MODE);
378   if (r == 0)
379     announce_mkdir (dir, options);
380   return r;
381 }
382 
383 /* Process a command-line file name, for the -d option.  */
384 static int
process_dir(char * dir,struct savewd * wd,void * options)385 process_dir (char *dir, struct savewd *wd, void *options)
386 {
387   struct cp_options const *x = options;
388 
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);
394 
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)
401     {
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));
406     }
407 
408   return ret;
409 }
410 
411 /* Copy file FROM onto file TO aka TO_DIRFD+TO_RELNAME, creating TO if
412    necessary.  Return true if successful.  */
413 
414 static bool
copy_file(char const * from,char const * to,int to_dirfd,char const * to_relname,const struct cp_options * x)415 copy_file (char const *from, char const *to,
416            int to_dirfd, char const *to_relname, const struct cp_options *x)
417 {
418   bool copy_into_self;
419 
420   if (copy_only_if_needed && !need_copy (from, to, to_dirfd, to_relname, x))
421     return true;
422 
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.  */
428 
429   return copy (from, to, to_dirfd, to_relname, 0, x, &copy_into_self, nullptr);
430 }
431 
432 /* Set the attributes of file or directory NAME aka DIRFD+RELNAME.
433    Return true if successful.  */
434 
435 static bool
change_attributes(char const * name,int dirfd,char const * relname)436 change_attributes (char const *name, int dirfd, char const *relname)
437 {
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.
444 
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.  */
450 
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;
458 
459   if (use_default_selinux_context)
460     setdefaultfilecon (name);
461 
462   return ok;
463 }
464 
465 /* Set the timestamps of file DEST aka DIRFD+RELNAME to match those of SRC_SB.
466    Return true if successful.  */
467 
468 static bool
change_timestamps(struct stat const * src_sb,char const * dest,int dirfd,char const * relname)469 change_timestamps (struct stat const *src_sb, char const *dest,
470                    int dirfd, char const *relname)
471 {
472   struct timespec timespec[2];
473   timespec[0] = get_stat_atime (src_sb);
474   timespec[1] = get_stat_mtime (src_sb);
475 
476   if (utimensat (dirfd, relname, timespec, 0))
477     {
478       error (0, errno, _("cannot set timestamps for %s"), quoteaf (dest));
479       return false;
480     }
481   return true;
482 }
483 
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. */
489 
490 static bool
strip(char const * name)491 strip (char const *name)
492 {
493   int status;
494   bool ok = false;
495   pid_t pid = fork ();
496 
497   switch (pid)
498     {
499     case -1:
500       error (0, errno, _("fork system call failed"));
501       break;
502     case 0:			/* Child. */
503       {
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));
510       }
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;
519     }
520   return ok;
521 }
522 
523 /* Initialize the user and group ownership of the files to install. */
524 
525 static void
get_ids(void)526 get_ids (void)
527 {
528   struct passwd *pw;
529   struct group *gr;
530 
531   if (owner_name)
532     {
533       pw = getpwnam (owner_name);
534       if (pw == nullptr)
535         {
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;
542         }
543       else
544         owner_id = pw->pw_uid;
545       endpwent ();
546     }
547   else
548     owner_id = (uid_t) -1;
549 
550   if (group_name)
551     {
552       gr = getgrnam (group_name);
553       if (gr == nullptr)
554         {
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;
561         }
562       else
563         group_id = gr->gr_gid;
564       endgrent ();
565     }
566   else
567     group_id = (gid_t) -1;
568 }
569 
570 void
usage(int status)571 usage (int status)
572 {
573   if (status != EXIT_SUCCESS)
574     emit_try_help ();
575   else
576     {
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\
582 "),
583               program_name, program_name, program_name, program_name);
584       fputs (_("\
585 \n\
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\
590 \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);
595 
596       emit_mandatory_arg_note ();
597 
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);
640 
641       fputs (HELP_OPTION_DESCRIPTION, stdout);
642       fputs (VERSION_OPTION_DESCRIPTION, stdout);
643       emit_backup_suffix_note ();
644       emit_ancillary_info (PROGRAM_NAME);
645     }
646   exit (status);
647 }
648 
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.  */
652 
653 static bool
install_file_in_file(char const * from,char const * to,int to_dirfd,char const * to_relname,const struct cp_options * x)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)
657 {
658   struct stat from_sb;
659   if (x->preserve_timestamps && stat (from, &from_sb) != 0)
660     {
661       error (0, errno, _("cannot stat %s"), quoteaf (from));
662       return false;
663     }
664   if (! copy_file (from, to, to_dirfd, to_relname, x))
665     return false;
666   if (strip_files)
667     if (! strip (to))
668       {
669         if (unlinkat (to_dirfd, to_relname, 0) != 0)  /* Cleanup.  */
670           error (EXIT_FAILURE, errno, _("cannot unlink %s"), quoteaf (to));
671         return false;
672       }
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);
677 }
678 
679 /* Create any missing parent directories of TO,
680    while maintaining the current Working Directory.
681    Return true if successful.  */
682 
683 static bool
mkancesdirs_safe_wd(char const * from,char * to,struct cp_options * x,bool save_always)684 mkancesdirs_safe_wd (char const *from, char *to, struct cp_options *x,
685                      bool save_always)
686 {
687   bool save_working_directory =
688     save_always
689     || ! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
690   int status = EXIT_SUCCESS;
691 
692   struct savewd wd;
693   savewd_init (&wd);
694   if (! save_working_directory)
695     savewd_finish (&wd);
696 
697   if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
698     {
699       error (0, errno, _("cannot create directory %s"), quoteaf (to));
700       status = EXIT_FAILURE;
701     }
702 
703   if (save_working_directory)
704     {
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)
711         {
712           error (0, restore_errno, _("cannot create directory %s"),
713                  quoteaf (to));
714           return false;
715         }
716     }
717   return status == EXIT_SUCCESS;
718 }
719 
720 /* Copy file FROM onto file TO, creating any missing parent directories of TO.
721    Return true if successful.  */
722 
723 static bool
install_file_in_file_parents(char const * from,char * to,const struct cp_options * x)724 install_file_in_file_parents (char const *from, char *to,
725                               const struct cp_options *x)
726 {
727   return (mkancesdirs_safe_wd (from, to, (struct cp_options *)x, false)
728           && install_file_in_file (from, to, AT_FDCWD, to, x));
729 }
730 
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.  */
734 
735 static bool
install_file_in_dir(char const * from,char const * to_dir,const struct cp_options * x,bool mkdir_and_install,int * target_dirfd)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)
739 {
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;
744 
745   if (!target_dirfd_valid (*target_dirfd)
746       && (ret = mkdir_and_install)
747       && (ret = mkancesdirs_safe_wd (from, to, (struct cp_options *) x, true)))
748     {
749       int fd = open (to_dir, O_PATHSEARCH | O_DIRECTORY);
750       if (fd < 0)
751         {
752           error (0, errno, _("cannot open %s"), quoteaf (to));
753           ret = false;
754         }
755       else
756         *target_dirfd = fd;
757     }
758 
759   if (ret)
760     {
761       int to_dirfd = *target_dirfd;
762       if (!target_dirfd_valid (to_dirfd))
763         {
764           to_dirfd = AT_FDCWD;
765           to_relname = to;
766         }
767       ret = install_file_in_file (from, to, to_dirfd, to_relname, x);
768     }
769 
770   free (to);
771   return ret;
772 }
773 
774 int
main(int argc,char ** argv)775 main (int argc, char **argv)
776 {
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 ());
793 
794   initialize_main (&argc, &argv);
795   set_program_name (argv[0]);
796   setlocale (LC_ALL, "");
797   bindtextdomain (PACKAGE, LOCALEDIR);
798   textdomain (PACKAGE);
799 
800   atexit (close_stdin);
801 
802   cp_option_init (&x);
803 
804   owner_name = nullptr;
805   group_name = nullptr;
806   strip_files = false;
807   dir_arg = false;
808   umask (0);
809 
810   while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options,
811                               nullptr))
812          != -1)
813     {
814       switch (optc)
815         {
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;
874 
875         case PRESERVE_CONTEXT_OPTION:
876           if (! selinux_enabled)
877             {
878               error (0, 0, _("WARNING: ignoring --preserve-context; "
879                              "this kernel is not SELinux-enabled"));
880               break;
881             }
882           x.preserve_security_context = true;
883           use_default_selinux_context = false;
884           break;
885         case 'Z':
886           if (selinux_enabled)
887             {
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;
895 
896               if (optarg)
897                 scontext = optarg;
898               else
899                 x.set_security_context = get_labeling_handle ();
900             }
901           else if (optarg)
902             {
903               error (0, 0,
904                      _("warning: ignoring --context; "
905                        "it requires an SELinux-enabled kernel"));
906             }
907           break;
908         case_GETOPT_HELP_CHAR;
909         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
910         default:
911           usage (EXIT_FAILURE);
912         }
913     }
914 
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"));
922 
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);
928 
929   if (x.preserve_security_context && (x.set_security_context || scontext))
930     error (EXIT_FAILURE, 0,
931            _("cannot set target context and preserve it"));
932 
933   if (scontext && setfscreatecon (scontext) < 0)
934     error (EXIT_FAILURE, errno,
935            _("failed to set default file creation context to %s"),
936          quote (scontext));
937 
938   n_files = argc - optind;
939   file = argv + optind;
940 
941   if (n_files <= ! (dir_arg || target_directory))
942     {
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);
949     }
950 
951   struct stat sb;
952   int target_dirfd = AT_FDCWD;
953   if (no_target_directory)
954     {
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)
960         {
961           error (0, 0, _("extra operand %s"), quoteaf (file[2]));
962           usage (EXIT_FAILURE);
963         }
964     }
965   else if (target_directory)
966     {
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));
972     }
973   else if (!dir_arg)
974     {
975       char const *lastfile = file[n_files - 1];
976       int fd = target_directory_operand (lastfile, &sb);
977       if (target_dirfd_valid (fd))
978         {
979           target_dirfd = fd;
980           target_directory = lastfile;
981           n_files--;
982         }
983       else if (2 < n_files)
984         error (EXIT_FAILURE, errno, _("target %s"), quoteaf (lastfile));
985     }
986 
987   if (specified_mode)
988     {
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);
995     }
996 
997   if (strip_program_specified && !strip_files)
998     error (0, 0, _("WARNING: ignoring --strip-program option as -s option was "
999                    "not specified"));
1000 
1001   if (copy_only_if_needed && x.preserve_timestamps)
1002     {
1003       error (0, 0, _("options --compare (-C) and --preserve-timestamps are "
1004                      "mutually exclusive"));
1005       usage (EXIT_FAILURE);
1006     }
1007 
1008   if (copy_only_if_needed && strip_files)
1009     {
1010       error (0, 0, _("options --compare (-C) and --strip are mutually "
1011                      "exclusive"));
1012       usage (EXIT_FAILURE);
1013     }
1014 
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"));
1018 
1019   get_ids ();
1020 
1021   if (dir_arg)
1022     exit_status = savewd_process_files (n_files, file, process_dir, &x);
1023   else
1024     {
1025       /* FIXME: it's a little gross that this initialization is
1026          required by copy.c::copy. */
1027       hash_init ();
1028 
1029       if (!target_directory)
1030         {
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;
1036         }
1037       else
1038         {
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;
1046         }
1047     }
1048 
1049   main_exit (exit_status);
1050 }
1051