1 /* $NetBSD: xinstall.c,v 1.7 1994/12/20 01:24:38 jtc Exp $ */ 2 3 /* 4 * Copyright (c) 1987, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 static char copyright[] = 38 "@(#) Copyright (c) 1987, 1993\n\ 39 The Regents of the University of California. All rights reserved.\n"; 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; 45 #endif 46 static char rcsid[] = "$NetBSD: xinstall.c,v 1.7 1994/12/20 01:24:38 jtc Exp $"; 47 #endif /* not lint */ 48 49 #include <sys/param.h> 50 #include <sys/wait.h> 51 #include <sys/mman.h> 52 #include <sys/stat.h> 53 54 #include <ctype.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <grp.h> 58 #include <paths.h> 59 #include <pwd.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 #include <err.h> 65 66 #include "pathnames.h" 67 68 struct passwd *pp; 69 struct group *gp; 70 int docopy, dodir, dostrip; 71 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 72 char *group, *owner, pathbuf[MAXPATHLEN]; 73 74 #define DIRECTORY 0x01 /* Tell install it's a directory. */ 75 #define SETFLAGS 0x02 /* Tell install to set flags. */ 76 77 void copy __P((int, char *, int, char *, off_t)); 78 void install __P((char *, char *, u_long, u_int)); 79 void install_dir __P((char *)); 80 u_long string_to_flags __P((char **, u_long *, u_long *)); 81 void strip __P((char *)); 82 void usage __P((void)); 83 84 int 85 main(argc, argv) 86 int argc; 87 char *argv[]; 88 { 89 struct stat from_sb, to_sb; 90 mode_t *set; 91 u_long fset; 92 u_int iflags; 93 int ch, no_target; 94 char *flags, *to_name; 95 96 iflags = 0; 97 while ((ch = getopt(argc, argv, "cf:g:m:o:sd")) != EOF) 98 switch((char)ch) { 99 case 'c': 100 docopy = 1; 101 break; 102 case 'f': 103 flags = optarg; 104 if (string_to_flags(&flags, &fset, NULL)) 105 errx(1, "%s: invalid flag", flags); 106 iflags |= SETFLAGS; 107 break; 108 case 'g': 109 group = optarg; 110 break; 111 case 'm': 112 if (!(set = setmode(optarg))) 113 errx(1, "%s: invalid file mode", optarg); 114 mode = getmode(set, 0); 115 break; 116 case 'o': 117 owner = optarg; 118 break; 119 case 's': 120 dostrip = 1; 121 break; 122 case 'd': 123 dodir = 1; 124 break; 125 case '?': 126 default: 127 usage(); 128 } 129 argc -= optind; 130 argv += optind; 131 132 /* copy and strip options make no sense when creating directories */ 133 if ((docopy || dostrip) && dodir) 134 usage(); 135 136 /* must have at least two arguments, except when creating directories */ 137 if (argc < 2 && !dodir) 138 usage(); 139 140 /* get group and owner id's */ 141 if (group && !(gp = getgrnam(group))) 142 errx(1, "unknown group %s", group); 143 if (owner && !(pp = getpwnam(owner))) 144 errx(1, "unknown user %s", owner); 145 146 if (dodir) { 147 for (; *argv != NULL; ++argv) 148 install_dir(*argv); 149 exit (0); 150 /* NOTREACHED */ 151 } 152 153 no_target = stat(to_name = argv[argc - 1], &to_sb); 154 if (!no_target && S_ISDIR(to_sb.st_mode)) { 155 for (; *argv != to_name; ++argv) 156 install(*argv, to_name, fset, iflags | DIRECTORY); 157 exit(0); 158 } 159 160 /* can't do file1 file2 directory/file */ 161 if (argc != 2) 162 usage(); 163 164 if (!no_target) { 165 if (stat(*argv, &from_sb)) 166 err(1, "%s", *argv); 167 if (!S_ISREG(to_sb.st_mode)) 168 errx(1, "%s: %s", to_name, strerror(EFTYPE)); 169 if (to_sb.st_dev == from_sb.st_dev && 170 to_sb.st_ino == from_sb.st_ino) 171 errx(1, "%s and %s are the same file", *argv, to_name); 172 /* 173 * Unlink now... avoid ETXTBSY errors later. Try and turn 174 * off the append/immutable bits -- if we fail, go ahead, 175 * it might work. 176 */ 177 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) 178 if (to_sb.st_flags & NOCHANGEBITS) 179 (void)chflags(to_name, 180 to_sb.st_flags & ~(NOCHANGEBITS)); 181 (void)unlink(to_name); 182 } 183 install(*argv, to_name, fset, iflags); 184 exit(0); 185 } 186 187 /* 188 * install -- 189 * build a path name and install the file 190 */ 191 void 192 install(from_name, to_name, fset, flags) 193 char *from_name, *to_name; 194 u_long fset; 195 u_int flags; 196 { 197 struct stat from_sb, to_sb; 198 int devnull, from_fd, to_fd, serrno; 199 char *p; 200 201 /* If try to install NULL file to a directory, fails. */ 202 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 203 if (stat(from_name, &from_sb)) 204 err(1, "%s", from_name); 205 if (!S_ISREG(from_sb.st_mode)) 206 errx(1, "%s: %s", from_name, strerror(EFTYPE)); 207 /* Build the target path. */ 208 if (flags & DIRECTORY) { 209 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 210 to_name, 211 (p = rindex(from_name, '/')) ? ++p : from_name); 212 to_name = pathbuf; 213 } 214 devnull = 0; 215 } else { 216 from_sb.st_flags = 0; /* XXX */ 217 devnull = 1; 218 } 219 220 /* 221 * Unlink now... avoid ETXTBSY errors later. Try and turn 222 * off the append/immutable bits -- if we fail, go ahead, 223 * it might work. 224 */ 225 if (stat(to_name, &to_sb) == 0 && 226 to_sb.st_flags & (NOCHANGEBITS)) 227 (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); 228 (void)unlink(to_name); 229 230 /* Create target. */ 231 if ((to_fd = open(to_name, 232 O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) 233 err(1, "%s", to_name); 234 if (!devnull) { 235 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { 236 (void)unlink(to_name); 237 err(1, "%s", from_name); 238 } 239 copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); 240 (void)close(from_fd); 241 } 242 if (dostrip) 243 strip(to_name); 244 /* 245 * Set owner, group, mode for target; do the chown first, 246 * chown may lose the setuid bits. 247 */ 248 if ((group || owner) && 249 fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) { 250 serrno = errno; 251 (void)unlink(to_name); 252 errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno)); 253 } 254 if (fchmod(to_fd, mode)) { 255 serrno = errno; 256 (void)unlink(to_name); 257 errx(1, "%s: chmod: %s", to_name, strerror(serrno)); 258 } 259 260 /* 261 * If provided a set of flags, set them, otherwise, preserve the 262 * flags, except for the dump flag. 263 */ 264 if (fchflags(to_fd, 265 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 266 serrno = errno; 267 (void)unlink(to_name); 268 errx(1, "%s: chflags: %s", to_name, strerror(serrno)); 269 } 270 271 (void)close(to_fd); 272 if (!docopy && !devnull && unlink(from_name)) 273 err(1, "%s", from_name); 274 } 275 276 /* 277 * copy -- 278 * copy from one file to another 279 */ 280 void 281 copy(from_fd, from_name, to_fd, to_name, size) 282 register int from_fd, to_fd; 283 char *from_name, *to_name; 284 off_t size; 285 { 286 register int nr, nw; 287 int serrno; 288 char *p, buf[MAXBSIZE]; 289 290 /* 291 * Mmap and write if less than 8M (the limit is so we don't totally 292 * trash memory on big files. This is really a minor hack, but it 293 * wins some CPU back. 294 */ 295 if (size <= 8 * 1048576) { 296 if ((p = mmap(NULL, (size_t)size, PROT_READ, 297 0, from_fd, (off_t)0)) == (char *)-1) 298 err(1, "%s", from_name); 299 if (write(to_fd, p, size) != size) 300 err(1, "%s", to_name); 301 } else { 302 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) 303 if ((nw = write(to_fd, buf, nr)) != nr) { 304 serrno = errno; 305 (void)unlink(to_name); 306 errx(1, "%s: %s", 307 to_name, strerror(nw > 0 ? EIO : serrno)); 308 } 309 if (nr != 0) { 310 serrno = errno; 311 (void)unlink(to_name); 312 errx(1, "%s: %s", from_name, strerror(serrno)); 313 } 314 } 315 } 316 317 /* 318 * strip -- 319 * use strip(1) to strip the target file 320 */ 321 void 322 strip(to_name) 323 char *to_name; 324 { 325 int serrno, status; 326 327 switch (vfork()) { 328 case -1: 329 serrno = errno; 330 (void)unlink(to_name); 331 errx(1, "forks: %s", strerror(errno)); 332 case 0: 333 execl(_PATH_STRIP, "strip", to_name, NULL); 334 err(1, "%s", _PATH_STRIP); 335 default: 336 if (wait(&status) == -1 || status) 337 (void)unlink(to_name); 338 } 339 } 340 341 /* 342 * install_dir -- 343 * build directory heirarchy 344 */ 345 void 346 install_dir(path) 347 char *path; 348 { 349 register char *p; 350 struct stat sb; 351 int ch; 352 353 for (p = path;; ++p) 354 if (!*p || (p != path && *p == '/')) { 355 ch = *p; 356 *p = '\0'; 357 if (stat(path, &sb)) { 358 if (errno != ENOENT || mkdir(path, 0777) < 0) { 359 err(1, "%s", path); 360 /* NOTREACHED */ 361 } 362 } 363 if (!(*p = ch)) 364 break; 365 } 366 367 if ((group || owner) && 368 chown(path, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1) || 369 chmod(path, mode)) { 370 warn("%s", path); 371 } 372 } 373 374 /* 375 * usage -- 376 * print a usage message and die 377 */ 378 void 379 usage() 380 { 381 (void)fprintf(stderr, "\ 382 usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\ 383 install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 ... fileN directory\n\ 384 install -d [-g group] [-m mode] [-o owner] directory ...\n"); 385 exit(1); 386 } 387