source: trunk/coreutils/src/install.c@ 2603

Last change on this file since 2603 was 2554, checked in by bird, 20 years ago

coretuils-5.94

File size: 19.2 KB
Line 
1/* install - copy files and set attributes
2 Copyright (C) 89, 90, 91, 1995-2005 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 2, or (at your option)
7 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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
17
18/* Written by David MacKenzie <[email protected]> */
19
20#include <config.h>
21#include <stdio.h>
22#include <getopt.h>
23#include <sys/types.h>
24#include <signal.h>
25#include <pwd.h>
26#include <grp.h>
27
28#include "system.h"
29#include "backupfile.h"
30#include "error.h"
31#include "cp-hash.h"
32#include "copy.h"
33#include "dirname.h"
34#include "filenamecat.h"
35#include "mkdir-p.h"
36#include "modechange.h"
37#include "quote.h"
38#include "stat-time.h"
39#include "utimens.h"
40#include "xstrtol.h"
41
42/* The official name of this program (e.g., no `g' prefix). */
43#define PROGRAM_NAME "install"
44
45#define AUTHORS "David MacKenzie"
46
47#if HAVE_SYS_WAIT_H
48# include <sys/wait.h>
49#endif
50
51#if ! HAVE_ENDGRENT
52# define endgrent() ((void) 0)
53#endif
54
55#if ! HAVE_ENDPWENT
56# define endpwent() ((void) 0)
57#endif
58
59/* Initial number of entries in each hash table entry's table of inodes. */
60#define INITIAL_HASH_MODULE 100
61
62/* Initial number of entries in the inode hash table. */
63#define INITIAL_ENTRY_TAB_SIZE 70
64
65/* Number of bytes of a file to copy at a time. */
66#define READ_SIZE (32 * 1024)
67
68static bool change_timestamps (struct stat const *from_sb, char const *to);
69static bool change_attributes (char const *name);
70static bool copy_file (const char *from, const char *to,
71 const struct cp_options *x);
72static bool install_file_in_file_parents (char const *from, char const *to,
73 struct cp_options const *x);
74static bool install_file_in_dir (const char *from, const char *to_dir,
75 const struct cp_options *x);
76static bool install_file_in_file (const char *from, const char *to,
77 const struct cp_options *x);
78static void get_ids (void);
79static void strip (char const *name);
80void usage (int status);
81
82/* The name this program was run with, for error messages. */
83char *program_name;
84
85/* The user name that will own the files, or NULL to make the owner
86 the current user ID. */
87static char *owner_name;
88
89/* The user ID corresponding to `owner_name'. */
90static uid_t owner_id;
91
92/* The group name that will own the files, or NULL to make the group
93 the current group ID. */
94static char *group_name;
95
96/* The group ID corresponding to `group_name'. */
97static gid_t group_id;
98
99/* The permissions to which the files will be set. The umask has
100 no effect. */
101static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
102
103/* If true, strip executable files after copying them. */
104static bool strip_files;
105
106/* If true, install a directory instead of a regular file. */
107static bool dir_arg;
108
109static struct option const long_options[] =
110{
111 {"backup", optional_argument, NULL, 'b'},
112 {"directory", no_argument, NULL, 'd'},
113 {"group", required_argument, NULL, 'g'},
114 {"mode", required_argument, NULL, 'm'},
115 {"no-target-directory", no_argument, NULL, 'T'},
116 {"owner", required_argument, NULL, 'o'},
117 {"preserve-timestamps", no_argument, NULL, 'p'},
118 {"strip", no_argument, NULL, 's'},
119 {"suffix", required_argument, NULL, 'S'},
120 {"target-directory", required_argument, NULL, 't'},
121 {"verbose", no_argument, NULL, 'v'},
122 {GETOPT_HELP_OPTION_DECL},
123 {GETOPT_VERSION_OPTION_DECL},
124 {NULL, 0, NULL, 0}
125};
126
127static void
128cp_option_init (struct cp_options *x)
129{
130 x->copy_as_regular = true;
131 x->dereference = DEREF_ALWAYS;
132 x->unlink_dest_before_opening = true;
133 x->unlink_dest_after_failed_open = false;
134 x->hard_link = false;
135 x->interactive = I_UNSPECIFIED;
136 x->move_mode = false;
137 x->chown_privileges = chown_privileges ();
138 x->one_file_system = false;
139 x->preserve_ownership = false;
140 x->preserve_links = false;
141 x->preserve_mode = false;
142 x->preserve_timestamps = false;
143 x->require_preserve = false;
144 x->recursive = false;
145 x->sparse_mode = SPARSE_AUTO;
146 x->symbolic_link = false;
147 x->backup_type = no_backups;
148
149 /* Create destination files initially writable so we can run strip on them.
150 Although GNU strip works fine on read-only files, some others
151 would fail. */
152 x->set_mode = true;
153 x->mode = S_IRUSR | S_IWUSR;
154 x->stdin_tty = false;
155
156 x->umask_kill = 0;
157 x->update = false;
158 x->verbose = false;
159 x->dest_info = NULL;
160 x->src_info = NULL;
161}
162
163/* FILE is the last operand of this command. Return true if FILE is a
164 directory. But report an error there is a problem accessing FILE,
165 or if FILE does not exist but would have to refer to an existing
166 directory if it referred to anything at all. */
167
168static bool
169target_directory_operand (char const *file)
170{
171 char const *b = base_name (file);
172 size_t blen = strlen (b);
173 bool looks_like_a_dir = (blen == 0 || ISSLASH (b[blen - 1]));
174 struct stat st;
175 int err = (stat (file, &st) == 0 ? 0 : errno);
176 bool is_a_dir = !err && S_ISDIR (st.st_mode);
177 if (err && err != ENOENT)
178 error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
179 if (is_a_dir < looks_like_a_dir)
180 error (EXIT_FAILURE, err, _("target %s is not a directory"), quote (file));
181 return is_a_dir;
182}
183
184int
185main (int argc, char **argv)
186{
187 int optc;
188 bool ok = true;
189 const char *specified_mode = NULL;
190 bool make_backups = false;
191 char *backup_suffix_string;
192 char *version_control_string = NULL;
193 bool mkdir_and_install = false;
194 struct cp_options x;
195 char const *target_directory = NULL;
196 bool no_target_directory = false;
197 int n_files;
198 char **file;
199
200 initialize_main (&argc, &argv);
201 program_name = argv[0];
202 setlocale (LC_ALL, "");
203 bindtextdomain (PACKAGE, LOCALEDIR);
204 textdomain (PACKAGE);
205
206 atexit (close_stdout);
207
208 cp_option_init (&x);
209
210 owner_name = NULL;
211 group_name = NULL;
212 strip_files = false;
213 dir_arg = false;
214 umask (0);
215
216 /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
217 we'll actually use backup_suffix_string. */
218 backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
219
220 while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pt:TvS:", long_options,
221 NULL)) != -1)
222 {
223 switch (optc)
224 {
225 case 'b':
226 make_backups = true;
227 if (optarg)
228 version_control_string = optarg;
229 break;
230 case 'c':
231 break;
232 case 's':
233 strip_files = true;
234#ifdef SIGCHLD
235 /* System V fork+wait does not work if SIGCHLD is ignored. */
236 signal (SIGCHLD, SIG_DFL);
237#endif
238 break;
239 case 'd':
240 dir_arg = true;
241 break;
242 case 'D':
243 mkdir_and_install = true;
244 break;
245 case 'v':
246 x.verbose = true;
247 break;
248 case 'g':
249 group_name = optarg;
250 break;
251 case 'm':
252 specified_mode = optarg;
253 break;
254 case 'o':
255 owner_name = optarg;
256 break;
257 case 'p':
258 x.preserve_timestamps = true;
259 break;
260 case 'S':
261 make_backups = true;
262 backup_suffix_string = optarg;
263 break;
264 case 't':
265 if (target_directory)
266 error (EXIT_FAILURE, 0,
267 _("multiple target directories specified"));
268 else
269 {
270 struct stat st;
271 if (stat (optarg, &st) != 0)
272 error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
273 if (! S_ISDIR (st.st_mode))
274 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
275 quote (optarg));
276 }
277 target_directory = optarg;
278 break;
279 case 'T':
280 no_target_directory = true;
281 break;
282 case_GETOPT_HELP_CHAR;
283 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
284 default:
285 usage (EXIT_FAILURE);
286 }
287 }
288
289 /* Check for invalid combinations of arguments. */
290 if (dir_arg & strip_files)
291 error (EXIT_FAILURE, 0,
292 _("the strip option may not be used when installing a directory"));
293 if (dir_arg && target_directory)
294 error (EXIT_FAILURE, 0,
295 _("target directory not allowed when installing a directory"));
296
297 if (backup_suffix_string)
298 simple_backup_suffix = xstrdup (backup_suffix_string);
299
300 x.backup_type = (make_backups
301 ? xget_version (_("backup type"),
302 version_control_string)
303 : no_backups);
304
305 n_files = argc - optind;
306 file = argv + optind;
307
308 if (n_files <= ! (dir_arg || target_directory))
309 {
310 if (n_files <= 0)
311 error (0, 0, _("missing file operand"));
312 else
313 error (0, 0, _("missing destination file operand after %s"),
314 quote (file[0]));
315 usage (EXIT_FAILURE);
316 }
317
318 if (no_target_directory)
319 {
320 if (target_directory)
321 error (EXIT_FAILURE, 0,
322 _("Cannot combine --target-directory (-t) "
323 "and --no-target-directory (-T)"));
324 if (2 < n_files)
325 {
326 error (0, 0, _("extra operand %s"), quote (file[2]));
327 usage (EXIT_FAILURE);
328 }
329 }
330 else if (! (dir_arg || target_directory))
331 {
332 if (2 <= n_files && target_directory_operand (file[n_files - 1]))
333 target_directory = file[--n_files];
334 else if (2 < n_files)
335 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
336 quote (file[n_files - 1]));
337 }
338
339 if (specified_mode)
340 {
341 struct mode_change *change = mode_compile (specified_mode);
342 if (!change)
343 error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode));
344 mode = mode_adjust (0, change, 0);
345 free (change);
346 }
347
348 get_ids ();
349
350 if (dir_arg)
351 {
352 int i;
353 int cwd_errno = 0;
354 for (i = 0; i < n_files; i++)
355 {
356 if (cwd_errno != 0 && IS_RELATIVE_FILE_NAME (file[i]))
357 {
358 error (0, cwd_errno, _("cannot return to working directory"));
359 ok = false;
360 }
361 else
362 ok &=
363 make_dir_parents (file[i], mode, mode, owner_id, group_id, false,
364 (x.verbose ? _("creating directory %s") : NULL),
365 &cwd_errno);
366 }
367 }
368 else
369 {
370 /* FIXME: it's a little gross that this initialization is
371 required by copy.c::copy. */
372 hash_init ();
373
374 if (!target_directory)
375 {
376 if (mkdir_and_install)
377 ok = install_file_in_file_parents (file[0], file[1], &x);
378 else
379 ok = install_file_in_file (file[0], file[1], &x);
380 }
381 else
382 {
383 int i;
384 dest_info_init (&x);
385 for (i = 0; i < n_files; i++)
386 {
387 ok &= install_file_in_dir (file[i], target_directory, &x);
388 }
389 }
390 }
391
392 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
393}
394
395/* Copy file FROM onto file TO, creating any missing parent directories of TO.
396 Return true if successful. */
397
398static bool
399install_file_in_file_parents (char const *from, char const *to,
400 struct cp_options const *x)
401{
402 char *dest_dir = dir_name (to);
403 bool ok = true;
404
405 /* Make sure that the parent of the destination is a directory. */
406 if (! STREQ (dest_dir, "."))
407 {
408 /* Someone will probably ask for a new option or three to specify
409 owner, group, and permissions for parent directories. Remember
410 that this option is intended mainly to help installers when the
411 distribution doesn't provide proper install rules. */
412 mode_t dir_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
413 int cwd_errno = 0;
414 ok = make_dir_parents (dest_dir, dir_mode, dir_mode,
415 owner_id, group_id, true,
416 (x->verbose ? _("creating directory %s") : NULL),
417 &cwd_errno);
418 if (ok && cwd_errno != 0
419 && (IS_RELATIVE_FILE_NAME (from) || IS_RELATIVE_FILE_NAME (to)))
420 {
421 error (0, cwd_errno, _("cannot return to current directory"));
422 ok = false;
423 }
424 }
425
426 free (dest_dir);
427
428 if (ok)
429 ok = install_file_in_file (from, to, x);
430
431 return ok;
432}
433
434/* Copy file FROM onto file TO and give TO the appropriate
435 attributes.
436 Return true if successful. */
437
438static bool
439install_file_in_file (const char *from, const char *to,
440 const struct cp_options *x)
441{
442 struct stat from_sb;
443 if (x->preserve_timestamps && stat (from, &from_sb) != 0)
444 {
445 error (0, errno, _("cannot stat %s"), quote (from));
446 return false;
447 }
448 if (! copy_file (from, to, x))
449 return false;
450 if (strip_files)
451 strip (to);
452 if (! change_attributes (to))
453 return false;
454 if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode)))
455 return change_timestamps (&from_sb, to);
456 return true;
457}
458
459/* Copy file FROM into directory TO_DIR, keeping its same name,
460 and give the copy the appropriate attributes.
461 Return true if successful. */
462
463static bool
464install_file_in_dir (const char *from, const char *to_dir,
465 const struct cp_options *x)
466{
467 const char *from_base = base_name (from);
468 char *to = file_name_concat (to_dir, from_base, NULL);
469 bool ret = install_file_in_file (from, to, x);
470 free (to);
471 return ret;
472}
473
474/* Copy file FROM onto file TO, creating TO if necessary.
475 Return true if successful. */
476
477static bool
478copy_file (const char *from, const char *to, const struct cp_options *x)
479{
480 bool copy_into_self;
481
482 /* Allow installing from non-regular files like /dev/null.
483 Charles Karney reported that some Sun version of install allows that
484 and that sendmail's installation process relies on the behavior.
485 However, since !x->recursive, the call to "copy" will fail if FROM
486 is a directory. */
487
488 return copy (from, to, false, x, &copy_into_self, NULL);
489}
490
491/* Set the attributes of file or directory NAME.
492 Return true if successful. */
493
494static bool
495change_attributes (char const *name)
496{
497 bool ok = true;
498
499 /* chown must precede chmod because on some systems,
500 chown clears the set[ug]id bits for non-superusers,
501 resulting in incorrect permissions.
502 On System V, users can give away files with chown and then not
503 be able to chmod them. So don't give files away.
504
505 We don't normally ignore errors from chown because the idea of
506 the install command is that the file is supposed to end up with
507 precisely the attributes that the user specified (or defaulted).
508 If the file doesn't end up with the group they asked for, they'll
509 want to know. But AFS returns EPERM when you try to change a
510 file's group; thus the kludge. */
511
512 if (chown (name, owner_id, group_id) != 0
513#ifdef AFS
514 && errno != EPERM
515#endif
516 )
517 {
518 error (0, errno, _("cannot change ownership of %s"), quote (name));
519 ok = false;
520 }
521
522 if (ok && chmod (name, mode) != 0)
523 {
524 error (0, errno, _("cannot change permissions of %s"), quote (name));
525 ok = false;
526 }
527
528 return ok;
529}
530
531/* Set the timestamps of file TO to match those of file FROM.
532 Return true if successful. */
533
534static bool
535change_timestamps (struct stat const *from_sb, char const *to)
536{
537 struct timespec timespec[2];
538 timespec[0] = get_stat_atime (from_sb);
539 timespec[1] = get_stat_mtime (from_sb);
540
541 if (utimens (to, timespec))
542 {
543 error (0, errno, _("cannot set time stamps for %s"), quote (to));
544 return false;
545 }
546 return true;
547}
548
549/* Strip the symbol table from the file NAME.
550 We could dig the magic number out of the file first to
551 determine whether to strip it, but the header files and
552 magic numbers vary so much from system to system that making
553 it portable would be very difficult. Not worth the effort. */
554
555static void
556strip (char const *name)
557{
558 int status;
559 pid_t pid = fork ();
560
561 switch (pid)
562 {
563 case -1:
564 error (EXIT_FAILURE, errno, _("fork system call failed"));
565 break;
566 case 0: /* Child. */
567 execlp ("strip", "strip", name, NULL);
568 error (EXIT_FAILURE, errno, _("cannot run strip"));
569 break;
570 default: /* Parent. */
571 /* Parent process. */
572 while (pid != wait (&status)) /* Wait for kid to finish. */
573 /* Do nothing. */ ;
574 if (status)
575 error (EXIT_FAILURE, 0, _("strip failed"));
576 break;
577 }
578}
579
580/* Initialize the user and group ownership of the files to install. */
581
582static void
583get_ids (void)
584{
585 struct passwd *pw;
586 struct group *gr;
587
588 if (owner_name)
589 {
590 pw = getpwnam (owner_name);
591 if (pw == NULL)
592 {
593 unsigned long int tmp;
594 if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
595 || UID_T_MAX < tmp)
596 error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name));
597 owner_id = tmp;
598 }
599 else
600 owner_id = pw->pw_uid;
601 endpwent ();
602 }
603 else
604 owner_id = (uid_t) -1;
605
606 if (group_name)
607 {
608 gr = getgrnam (group_name);
609 if (gr == NULL)
610 {
611 unsigned long int tmp;
612 if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
613 || GID_T_MAX < tmp)
614 error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name));
615 group_id = tmp;
616 }
617 else
618 group_id = gr->gr_gid;
619 endgrent ();
620 }
621 else
622 group_id = (gid_t) -1;
623}
624
625void
626usage (int status)
627{
628 if (status != EXIT_SUCCESS)
629 fprintf (stderr, _("Try `%s --help' for more information.\n"),
630 program_name);
631 else
632 {
633 printf (_("\
634Usage: %s [OPTION]... [-T] SOURCE DEST\n\
635 or: %s [OPTION]... SOURCE... DIRECTORY\n\
636 or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
637 or: %s [OPTION]... -d DIRECTORY...\n\
638"),
639 program_name, program_name, program_name, program_name);
640 fputs (_("\
641In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\
642the existing DIRECTORY, while setting permission modes and owner/group.\n\
643In the 4th form, create all components of the given DIRECTORY(ies).\n\
644\n\
645"), stdout);
646 fputs (_("\
647Mandatory arguments to long options are mandatory for short options too.\n\
648"), stdout);
649 fputs (_("\
650 --backup[=CONTROL] make a backup of each existing destination file\n\
651 -b like --backup but does not accept an argument\n\
652 -c (ignored)\n\
653 -d, --directory treat all arguments as directory names; create all\n\
654 components of the specified directories\n\
655"), stdout);
656 fputs (_("\
657 -D create all leading components of DEST except the last,\n\
658 then copy SOURCE to DEST\n\
659 -g, --group=GROUP set group ownership, instead of process' current group\n\
660 -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
661 -o, --owner=OWNER set ownership (super-user only)\n\
662"), stdout);
663 fputs (_("\
664 -p, --preserve-timestamps apply access/modification times of SOURCE files\n\
665 to corresponding destination files\n\
666 -s, --strip strip symbol tables\n\
667 -S, --suffix=SUFFIX override the usual backup suffix\n\
668 -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
669 -T, --no-target-directory treat DEST as a normal file\n\
670 -v, --verbose print the name of each directory as it is created\n\
671"), stdout);
672 fputs (HELP_OPTION_DESCRIPTION, stdout);
673 fputs (VERSION_OPTION_DESCRIPTION, stdout);
674 fputs (_("\
675\n\
676The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
677The version control method may be selected via the --backup option or through\n\
678the VERSION_CONTROL environment variable. Here are the values:\n\
679\n\
680"), stdout);
681 fputs (_("\
682 none, off never make backups (even if --backup is given)\n\
683 numbered, t make numbered backups\n\
684 existing, nil numbered if numbered backups exist, simple otherwise\n\
685 simple, never always make simple backups\n\
686"), stdout);
687 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
688 }
689 exit (status);
690}
Note: See TracBrowser for help on using the repository browser.