diff --git a/commands/Makefile b/commands/Makefile index 97c96af06..4dda5c878 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -13,7 +13,7 @@ SUBDIR= add_route arp ash at \ eject elvis env expand factor fbdctl \ find finger fingerd fix fold format fortune fsck.mfs \ ftp101 gcore gcov-pull getty grep head hexdump host \ - hostaddr id ifconfig ifdef install \ + hostaddr id ifconfig ifdef \ intr ipcrm ipcs irdpd isoread join kill last \ less loadkeys loadramdisk logger look lp \ lpd ls lspci mail MAKEDEV \ diff --git a/commands/install/Makefile b/commands/install/Makefile deleted file mode 100644 index 1c52030db..000000000 --- a/commands/install/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -PROG= xinstall -SRCS= install.c -BINDIR= /bin -BINMODE= 4755 -MAN= - -PROGNAME=install - -.include diff --git a/commands/install/install.c b/commands/install/install.c deleted file mode 100644 index b30f60480..000000000 --- a/commands/install/install.c +++ /dev/null @@ -1,657 +0,0 @@ -/* install 1.11 - install files. Author: Kees J. Bot - * 21 Feb 1993 - */ -#define nil 0 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* First line used on a self-decompressing executable. */ -char ZCAT[]= "#!/usr/bin/zexec /usr/bin/zcat\n"; -char GZCAT[]= "#!/usr/bin/zexec /usr/bin/gzcat\n"; - -/* Compression filters. */ -char *COMPRESS[]= { "compress", nil }; -char *GZIP[]= { "gzip", "-#", nil }; - -int excode= 0; /* Exit code. */ - -void report(char *label) -{ - if (label == nil || label[0] == 0) - fprintf(stderr, "install: %s\n", strerror(errno)); - else - fprintf(stderr, "install: %s: %s\n", label, strerror(errno)); - excode= 1; -} - -void fatal(char *label) -{ - report(label); - exit(1); -} - -void *allocate(void *mem, size_t size) -/* Safe malloc/realloc. */ -{ - mem= mem == nil ? malloc(size) : realloc(mem, size); - - if (mem == nil) fatal(nil); - return mem; -} - -void deallocate(void *mem) -{ - if (mem != nil) free(mem); -} - -int lflag= 0; /* Make a link if possible. */ -int cflag= 0; /* Copy if you can't link, otherwise symlink. */ -int dflag= 0; /* Create a directory. */ -int pflag= 0; /* Preserve timestamps. */ -int strip= 0; /* Strip the copy. */ -char **compress= nil; /* Compress utility to make a compressed executable. */ -char *zcat= nil; /* Line one to decompress. */ - -int make_hardlink = 0; /* Make a hard link */ -int make_symlink = 0; /* Make a symbolic link */ - -long stack= -1; /* Amount of heap + stack. */ -int wordpow= 1; /* Must be multiplied with wordsize ** wordpow */ - /* So 8kb for an 8086 and 16kb for the rest. */ - -pid_t filter(int fd, char **command) -/* Let a command filter the output to fd. */ -{ - pid_t pid; - int pfd[2]; - - if (pipe(pfd) < 0) { - report("pipe()"); - return -1; - } - - switch ((pid= fork())) { - case -1: - report("fork()"); - return -1; - case 0: - /* Run the filter. */ - dup2(pfd[0], 0); - dup2(fd, 1); - close(pfd[0]); - close(pfd[1]); - close(fd); - signal(SIGPIPE, SIG_DFL); - execvp(command[0], command); - fatal(command[0]); - } - /* Connect fd to the pipe. */ - dup2(pfd[1], fd); - close(pfd[0]); - close(pfd[1]); - return pid; -} - -int mkdirp(char *dir, int mode, int owner, int group) -/* mkdir -p dir */ -{ - int keep; - char *sep, *pref; - - sep= dir; - while (*sep == '/') sep++; - - if (*sep == 0) { - errno= EINVAL; - return -1; - } - - do { - while (*sep != '/' && *sep != 0) sep++; - pref= sep; - while (*sep == '/') sep++; - - keep= *pref; *pref= 0; - - if (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0) continue; - - if (mkdir(dir, mode) < 0) { - if (errno != EEXIST || *sep == 0) { - /* On purpose not doing: *pref= keep; */ - return -1; - } - } else { - if (chown(dir, owner, group) < 0 && errno != EPERM) - return -1; - } - } while (*pref= keep, *sep != 0); - return 0; -} - -void makedir(char *dir, int mode, int owner, int group) -/* Install a directory, and set it's modes. */ -{ - struct stat st; - - if (stat(dir, &st) < 0) { - if (errno != ENOENT) { report(dir); return; } - - /* The target doesn't exist, make it. */ - if (mode == -1) mode= 0755; - if (owner == -1) owner= getuid(); - if (group == -1) group= getgid(); - - if (mkdirp(dir, mode, owner, group) < 0) { - report(dir); return; - } - } else { - /* The target does exist, change mode and ownership. */ - if (mode == -1) mode= (st.st_mode & 07777) | 0555; - - if ((st.st_mode & 07777) != mode) { - if (chmod(dir, mode) < 0) { report(dir); return; } - } - if (owner == -1) owner= st.st_uid; - if (group == -1) group= st.st_gid; - if (st.st_uid != owner || st.st_gid != group) { - if (chown(dir, owner, group) < 0 && errno != EPERM) { - report(dir); return; - } - /* Set the mode again, chown may have wrecked it. */ - (void) chmod(dir, mode); - } - - if (pflag) { - struct utimbuf ubuf; - - ubuf.actime= st.st_atime; - ubuf.modtime= st.st_mtime; - - if (utime(dir, &ubuf) < 0 && errno != EPERM) { - report(dir); return; - } - } - } -} - -int setstack(struct exec *hdr) -/* Set the stack size in a header. Return true if something changed. */ -{ - long total; - - total= stack; - while (wordpow > 0) { - total *= hdr->a_cpu == A_I8086 ? 2 : 4; - wordpow--; - } - total+= hdr->a_data + hdr->a_bss; - - if (!(hdr->a_flags & A_SEP)) { - total+= hdr->a_text; -#ifdef A_PAL - if (hdr->a_flags & A_PAL) total+= hdr->a_hdrlen; -#endif - } - if (hdr->a_cpu == A_I8086 && total > 0x10000L) - total= 0x10000L; - - if (hdr->a_total != total) { - /* Need to change stack allocation. */ - hdr->a_total= total; - - return 1; - } - return 0; -} - -void copylink(char *source, char *dest, int mode, int owner, int group) -{ - struct stat sst, dst; - int sfd, dfd, n; - int r, same= 0, change= 0, docopy= 1; - char buf[4096]; -# define hdr ((struct exec *) buf) - pid_t pid = 0; - int status = 0; - char *p; - - /* Source must exist as a plain file, dest may exist as a plain file. */ - if (make_symlink && source[0] != '/' - && (p = strrchr(dest, '/')) != NULL ) { - /* creating a relative symlink; compute real target */ - strlcpy(buf, dest, sizeof(buf)); - if (p - dest + 1 >= (int)sizeof(buf)) - p = dest + sizeof(buf) - 2; - buf[p - dest + 1] = '\0'; - strlcat(buf, source, sizeof(buf)); - r = stat(buf, &sst); - } else - r = stat(source, &sst); - if (r < 0) { report(source); return; } - - if (mode == -1) { - mode= sst.st_mode & 07777; - if (!lflag || cflag) { - mode|= 0444; - if (mode & 0111) mode|= 0111; - } - } - if (owner == -1) owner= sst.st_uid; - if (group == -1) group= sst.st_gid; - - if (!make_symlink && !S_ISREG(sst.st_mode)) { - fprintf(stderr, "install: %s is not a regular file\n", source); - excode= 1; - return; - } - r= lstat(dest, &dst); - if (r < 0) { - if (errno != ENOENT) { report(dest); return; } - } else { - if (!make_symlink && !S_ISREG(dst.st_mode)) { - fprintf(stderr, "install: %s is not a regular file\n", - dest); - excode= 1; - return; - } - - /* Are the files the same? */ - if (sst.st_dev == dst.st_dev && sst.st_ino == dst.st_ino) { - if (!lflag && cflag) { - fprintf(stderr, - "install: %s and %s are the same, can't copy\n", - source, dest); - excode= 1; - return; - } - same= 1; - } - } - - if (lflag) { - /* Try to link the files. */ - - if (r >= 0 && unlink(dest) < 0) { - report(dest); return; - } - - if (make_hardlink) { - r = link(source, dest); - } - else if (make_symlink) { - r = symlink(source, dest); - } else { - fprintf(stderr, - "install: must specify hardlink or symlink\n"); - } - - if (r >= 0) { - docopy= 0; - } else { - if (!cflag || errno != EXDEV) { - fprintf(stderr, - "install: can't link %s to %s: %s\n", - source, dest, strerror(errno)); - excode= 1; - return; - } - } - } - - if (docopy && !same) { - /* Copy the files, stripping if necessary. */ - long count= LONG_MAX; - int first= 1; - - if ((sfd= open(source, O_RDONLY)) < 0) { - report(source); return; - } - - /* Open for write is less simple, its mode may be 444. */ - dfd= open(dest, O_WRONLY|O_CREAT|O_TRUNC, mode | 0600); - if (dfd < 0 && errno == EACCES) { - (void) chmod(dest, mode | 0600); - dfd= open(dest, O_WRONLY|O_TRUNC); - } - if (dfd < 0) { - report(dest); - close(sfd); - return; - } - - pid= 0; - while (count > 0 && (n= read(sfd, buf, sizeof(buf))) > 0) { - if (first && n >= A_MINHDR && !BADMAG(*hdr)) { - if (strip) { - count= hdr->a_hdrlen - + hdr->a_text + hdr->a_data; -#ifdef A_NSYM - hdr->a_flags &= ~A_NSYM; -#endif - hdr->a_syms= 0; - } - if (stack != -1 && setstack(hdr)) change= 1; - - if (compress != nil) { - /* Write first #! line. */ - (void) write(dfd, zcat, strlen(zcat)); - - /* Put a compressor in between. */ - if ((pid= filter(dfd, compress)) < 0) { - close(sfd); - close(dfd); - return; - } - change= 1; - } - } - if (count < n) n= count; - - if (write(dfd, buf, n) < 0) { - report(dest); - close(sfd); - close(dfd); - if (pid != 0) (void) waitpid(pid, nil, 0); - return; - } - count-= n; - first= 0; - } - if (n < 0) report(source); - close(sfd); - close(dfd); - if (pid != 0 && waitpid(pid, &status, 0) < 0 || status != 0) { - excode= 1; - return; - } - if (n < 0) return; - } else { - if (stack != -1) { - /* The file has been linked into place. Set the - * stack size. - */ - if ((dfd= open(dest, O_RDWR)) < 0) { - report(dest); - return; - } - - if ((n= read(dfd, buf, sizeof(*hdr))) < 0) { - report(dest); return; - } - - if (n >= A_MINHDR && !BADMAG(*hdr) && setstack(hdr)) { - if (lseek(dfd, (off_t) 0, SEEK_SET) == -1 - || write(dfd, buf, n) < 0 - ) { - report(dest); - close(dfd); - return; - } - change= 1; - } - close(dfd); - } - } - - /* Don't modify metadata if symlink target doesn't exist */ - if (make_symlink && stat(dest, &dst) < 0) return; - - if (stat(dest, &dst) < 0) { report(dest); return; } - - if ((dst.st_mode & 07777) != mode) { - if (chmod(dest, mode) < 0) { report(dest); return; } - } - if (dst.st_uid != owner || dst.st_gid != group) { - if (chown(dest, owner, group) < 0 && errno != EPERM) { - report(dest); return; - } - /* Set the mode again, chown may have wrecked it. */ - (void) chmod(dest, mode); - } - if (!change || pflag) { - struct utimbuf ubuf; - - ubuf.actime= dst.st_atime; - ubuf.modtime= sst.st_mtime; - - if (utime(dest, &ubuf) < 0 && errno != EPERM) { - report(dest); return; - } - } -} - -void usage(void) -{ - fprintf(stderr, "\ -Usage:\n\ - install [-lcpsz#] [-o owner] [-g group] [-m mode] [-S stack] [file1] file2\n\ - install [-lcpsz#] [-o owner] [-g group] [-m mode] [-S stack] file ... dir\n\ - install [-c] -d [-o owner] [-g group] [-m mode] directory\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - int i= 1; - int mode= -1; /* Mode of target. */ - int owner= -1; /* Owner. */ - int group= -1; /* Group. */ - int super = 0; -#if NGROUPS_MAX > 0 - gid_t groups[NGROUPS_MAX]; - int ngroups; - int g; -#endif - - /* Only those in group 0 are allowed to set owner and group. */ - if (getgid() == 0) super = 1; -#if NGROUPS_MAX > 0 - ngroups= getgroups(NGROUPS_MAX, groups); - for (g= 0; g < ngroups; g++) if (groups[g] == 0) super= 1; -#endif - if (!super) { - setgid(getgid()); - setuid(getuid()); - } - - /* May use a filter. */ - signal(SIGPIPE, SIG_IGN); - - while (i < argc && argv[i][0] == '-') { - char *p= argv[i++]+1; - char *end; - unsigned long num; - int wp; - struct passwd *pw; - struct group *gr; - - if (strcmp(p, "-") == 0) break; - - while (*p != 0) { - switch (*p++) { - case 'c': cflag= 1; break; - case 's': strip= 1; break; - case 'd': dflag= 1; break; - case 'p': pflag= 1; break; - case 'z': - if (compress == nil) { - compress= COMPRESS; - zcat= ZCAT; - } - break; - case 'o': - if (*p == 0) { - if (i == argc) usage(); - p= argv[i++]; - if (*p == 0) usage(); - } - num= strtoul(p, &end, 10); - if (*end == 0) { - if ((uid_t) num != num) usage(); - owner= num; - } else { - if ((pw= getpwnam(p)) == nil) { - fprintf(stderr, - "install: %s: unknown user\n", - p); - exit(1); - } - owner= pw->pw_uid; - } - p= ""; - break; - case 'g': - if (*p == 0) { - if (i == argc) usage(); - p= argv[i++]; - if (*p == 0) usage(); - } - num= strtoul(p, &end, 10); - if (*end == 0) { - if ((gid_t) num != num) usage(); - group= num; - } else { - if ((gr= getgrnam(p)) == nil) { - fprintf(stderr, - "install: %s: unknown group\n", - p); - exit(1); - } - group= gr->gr_gid; - } - p= ""; - break; - case 'm': - if (*p == 0) { - if (i == argc) usage(); - p= argv[i++]; - if (*p == 0) usage(); - } - num= strtoul(p, &end, 010); - if (*end != 0 || (num & 07777) != num) usage(); - mode= num; - if ((mode & S_ISUID) && super && owner == -1) { - /* Setuid what? Root most likely. */ - owner= 0; - } - if ((mode & S_ISGID) && super && group == -1) { - group= 0; - } - p= ""; - break; - case 'l': - lflag= 1; - if (*p == 0) { - if (i == argc) usage(); - p= argv[i++]; - if (*p == 0) usage(); - } - switch(*p) { - case 'h': - make_hardlink = 1; - break; - case 's': - make_symlink = 1; - break; - default: - break; - } - p= ""; - break; - case 'S': - if (*p == 0) { - if (i == argc) usage(); - p= argv[i++]; - if (*p == 0) usage(); - } - stack= strtol(p, &end, 0); - wp= 0; - if (end == p || stack < 0) usage(); - p= end; - while (*p != 0) { - switch (*p++) { - case 'm': - case 'M': num= 1024 * 1024L; break; - case 'k': - case 'K': num= 1024; break; - case 'w': - case 'W': num= 4; wp++; break; - case 'b': - case 'B': num= 1; break; - default: usage(); - } - if (stack > LONG_MAX / num) usage(); - stack*= num; - } - wordpow= 0; - while (wp > 0) { stack /= 4; wordpow++; wp--; } - break; - default: - if ((unsigned) (p[-1] - '1') <= ('9' - '1')) { - compress= GZIP; - GZIP[1][1]= p[-1]; - zcat= GZCAT; - break; - } - usage(); - } - } - } - /* Some options don't mix. */ - if (dflag && (lflag || strip)) usage(); - - /* Don't let the user umask interfere. */ - umask(000); - - if (dflag) { - /* install directory */ - if ((argc - i) != 1) usage(); - - makedir(argv[i], mode, owner, group); - } else { - struct stat st; - - if ((argc - i) < 1) usage(); - if ((lflag || cflag) && (argc - i) == 1) usage(); - - if (lstat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)) { - /* install file ... dir */ - char *target= nil; - char *base; - - if ((argc - i) == 1) usage(); - - while (i < argc-1) { - if ((base= strrchr(argv[i], '/')) == nil) - base= argv[i]; - else - base++; - target= allocate(target, strlen(argv[argc-1]) - + 1 + strlen(base) + 1); - strcpy(target, argv[argc-1]); - strcat(target, "/"); - strcat(target, base); - - copylink(argv[i++], target, mode, owner, group); - } - } else { - /* install [file1] file2 */ - - copylink(argv[i], argv[argc-1], mode, owner, group); - } - } - exit(excode); -} diff --git a/docs/UPDATING b/docs/UPDATING index 14cbc7305..337a40c64 100644 --- a/docs/UPDATING +++ b/docs/UPDATING @@ -1,3 +1,15 @@ +20120608: + New install and mk files require the following steps: + + # cp /usr/src/share/mk/*.mk /usr/share/mk + # cd /usr/src + # make -C usr.bin/xinstall all + # cp usr.bin/xinstall/xinstall /usr/bin/install + # rm /bin/install + + then do + # make clean world + 20120510: WARNING: the shared libraries major revision set to 0 will break existing dynamically linked binaries if they exist. diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 1c64f1fdd..97cb34045 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -56,6 +56,9 @@ TOOLCHAIN_MISSING?= no # default to GCC4 .if !defined(HAVE_GCC) && !defined(HAVE_PCC) HAVE_GCC= 4 +.if !defined(_GCC_LIBGCCDIR) +_GCC_LIBGCCDIR= ${DESTDIR}/usr/lib +.endif .endif .if \ @@ -128,8 +131,10 @@ _SRC_TOP_OBJ_!= cd ${_SRC_TOP_} && ${PRINTOBJDIR} .if (${_SRC_TOP_} != "") && \ (${TOOLCHAIN_MISSING} == "no" || defined(EXTERNAL_TOOLCHAIN)) +.if (defined(BUILDSH)) USETOOLS?= yes .endif +.endif USETOOLS?= no @@ -756,10 +761,11 @@ _MKVARS.no= \ MKMANDOC MKMANZ MKOBJDIRS \ MKPCC MKPCCCMDS \ MKSOFTFLOAT MKSTRIPIDENT \ - MKUNPRIVED MKUPDATE MKX11 MKZFS MKBSDTAR + MKUNPRIVED MKUPDATE MKX11 MKZFS MKBSDTAR \ + MKARZERO #MINIX-specific vars _MKVARS.no+= \ - MKIMAGEONLY MKSMALL MKBUILDEXT2RD + MKIMAGEONLY MKSMALL MKBUILDEXT2RD USETOOLS .for var in ${_MKVARS.no} ${var}?=no .endfor diff --git a/share/mk/sys.mk b/share/mk/sys.mk index 8ae1cf182..e94493577 100644 --- a/share/mk/sys.mk +++ b/share/mk/sys.mk @@ -6,7 +6,7 @@ unix?= We run MINIX. # This variable should be used to differentiate Minix builds in Makefiles. __MINIX= yes -.SUFFIXES: .a .o .ln .s .S .c .cc .cpp .cxx .C .f .F .r .p .l .y #.sh +.SUFFIXES: .a .o .ln .s .S .c .cc .cpp .cxx .C .f .F .r .p .l .y .sh .LIBS: .a diff --git a/usr.bin/Makefile b/usr.bin/Makefile index f2db6633d..29b4e9042 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -5,7 +5,7 @@ # NetBSD imports SUBDIR= login indent m4 make mktemp stat tic sed mkdep uniq seq du man \ apropos chpass newgrp passwd bzip2 bzip2recover gzip su genassym \ - ldd/elf32 .WAIT ldd + xinstall ldd/elf32 .WAIT ldd # Non-NetBSD imports SUBDIR+= ministat diff --git a/usr.bin/xinstall/Makefile b/usr.bin/xinstall/Makefile new file mode 100644 index 000000000..c1862e2a6 --- /dev/null +++ b/usr.bin/xinstall/Makefile @@ -0,0 +1,23 @@ +# $NetBSD: Makefile,v 1.22 2011/08/17 14:00:30 christos Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +.include + +PROG= xinstall +SRCS= xinstall.c getid.c +MAN= install.1 + +.PATH: ${NETBSDSRCDIR}/usr.sbin/mtree +CPPFLAGS+= -I${NETBSDSRCDIR}/usr.sbin/mtree + +.if (${HOSTPROG:U} == "") +DPADD+= ${LIBUTIL} +LDADD+= -lutil +.endif + +COPTS.xinstall.c += -Wno-format-nonliteral + + +PROGNAME=install + +.include diff --git a/usr.bin/xinstall/install.1 b/usr.bin/xinstall/install.1 new file mode 100644 index 000000000..796775957 --- /dev/null +++ b/usr.bin/xinstall/install.1 @@ -0,0 +1,338 @@ +.\" $NetBSD: install.1,v 1.47 2012/04/08 22:00:40 wiz Exp $ +.\" +.\" Copyright (c) 1987, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)install.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd May 1, 2009 +.Dt INSTALL 1 +.Os +.Sh NAME +.Nm install +.Nd install binaries +.Sh SYNOPSIS +.Nm +.Op Fl bcprsU +.Op Fl a Ar command +.Op Fl B Ar suffix +.Op Fl D Ar destdir +.Op Fl f Ar flags +.Op Fl g Ar group +.Op Fl h Ar hash +.Op Fl l Ar linkflags +.Op Fl M Ar metalog +.Op Fl m Ar mode +.Op Fl N Ar dbdir +.Op Fl o Ar owner +.Op Fl S Ar stripflag +.Op Fl T Ar tags +.Ar file1 file2 +.Nm +.Op Fl bcprsU +.Op Fl a Ar command +.Op Fl B Ar suffix +.Op Fl D Ar destdir +.Op Fl f Ar flags +.Op Fl g Ar group +.Op Fl h Ar hash +.Op Fl l Ar linkflags +.Op Fl M Ar metalog +.Op Fl m Ar mode +.Op Fl N Ar dbdir +.Op Fl o Ar owner +.Op Fl S Ar stripflag +.Op Fl T Ar tags +.Ar file1 ...\& +.Ar fileN directory +.Nm +.Fl d +.Op Fl pU +.Op Fl a Ar command +.Op Fl D Ar destdir +.Op Fl g Ar group +.Op Fl M Ar metalog +.Op Fl m Ar mode +.Op Fl N Ar dbdir +.Op Fl o Ar owner +.Op Fl T Ar tags +.Ar directory ...\& +.Sh DESCRIPTION +The file(s) are copied +(or linked if the +.Fl l +option is specified) to the target file or directory. +If the destination is a directory, then the +.Ar file +is copied into +.Ar directory +with its original filename. +If the target file already exists, it is +either renamed to +.Ar file.old +if the +.Fl b +option is given +or overwritten +if permissions allow; an alternate backup suffix may be specified via the +.Fl B +option's argument. +.Pp +.Bl -tag -width XsXXstripflagsXX +.It Fl a Ar command +Run +.Ar command +on the target after installation and stripping +.Pq Fl s , +but before +ownership, permissions or timestamps are set and before renaming +.Pq Fl r +occurs. +.Ar command +is invoked via the +.Xr sh 1 +shell, allowing a single +.Fl a +argument be to specified to +.Nm +which the shell can then tokenize. +.It Fl B Ar suffix +Use +.Ar suffix +as the backup suffix if +.Fl b +is given. +If +.Ar suffix +contains a '%' sign, a numbered backup will be performed, and the +%-pattern will be expanded using +.Xr sprintf 3 , +given an integer counter as the backup number. +The counter used starts from 0, and the first available name resulting +from the expansion is used. +.It Fl b +Backup any existing files before overwriting them by renaming +them to +.Ar file.old . See +.Fl B +for specifying a different backup suffix. +.It Fl c +Copy the file. +This is the default behavior; the flag is maintained for backwards +compatibility only. +.It Fl D Ar destdir +Specify the +.Ev DESTDIR +(top of the file hierarchy) that the items are installed in to. +If +.Fl M Ar metalog +is in use, a leading string of +.Dq Ar destdir +will be removed from the file names logged to the +.Ar metalog . +This option does not affect where the actual files are installed. +.It Fl d +Create directories. +Missing parent directories are created as required. +.It Fl f Ar flags +Specify the target's file flags. +(See +.Xr chflags 1 +for a list of possible flags and their meanings.) +.It Fl g Ar group +Specify a group. +.It Fl h Ar hash +When copying, calculate the digest of the files with +.Ar hash +to store in the +.Fl M Ar metalog . +Supported digests: +.Bl -tag -width rmd160 -offset indent +.It Sy none +No hash. +This is the default. +.It Sy md5 +The MD5 cryptographic message digest. +.It Sy rmd160 +The RMD-160 cryptographic message digest. +.It Sy sha1 +The SHA-1 cryptographic message digest. +.It Sy sha256 +The 256-bits +.Tn SHA-2 +cryptographic message digest of the file. +.It Sy sha384 +The 384-bits +.Tn SHA-2 +cryptographic message digest of the file. +.It Sy sha512 +The 512-bits +.Tn SHA-2 +cryptographic message digest of the file. +.El +.It Fl l Ar linkflags +Instead of copying the file make a link to the source. +The type of the link is determined by the +.Ar linkflags +argument. +Valid +.Ar linkflags +are: +.Ar a +(absolute), +.Ar r +(relative), +.Ar h +(hard), +.Ar s +(symbolic), +.Ar m +(mixed). +Absolute and relative have effect only for symbolic links. +Mixed links +are hard links for files on the same filesystem, symbolic otherwise. +.It Fl M Ar metalog +Write the metadata associated with each item installed to +.Ar metalog +in an +.Xr mtree 8 +.Dq full path +specification line. +The metadata includes: the file name and file type, and depending upon +other options, the owner, group, file flags, modification time, and tags. +.It Fl m Ar mode +Specify an alternative mode. +The default mode is set to rwxr-xr-x (0755). +The specified mode may be either an octal or symbolic value; see +.Xr chmod 1 +for a description of possible mode values. +.It Fl N Ar dbdir +Use the user database text file +.Pa master.passwd +and group database text file +.Pa group +from +.Ar dbdir , +rather than using the results from the system's +.Xr getpwnam 3 +and +.Xr getgrnam 3 +(and related) library calls. +.It Fl o Ar owner +Specify an owner. +.It Fl p +Preserve the source files access and modification times. +.It Fl r +Install to a temporary file and then rename the file to its final destination +name. +This can be used for precious files, to avoid truncation of the original +when error conditions (filesystem full etc.) occur. +.It Fl S Ar stripflags +.Nm +passes +.Ar stripflags +as option arguments to +.Xr strip 1 . +When +.Fl S +is used, +.Xr strip 1 +is invoked via the +.Xr sh 1 +shell, allowing a single +.Fl S +argument be to specified to +.Nm +which the shell can then tokenize. +Normally, +.Nm +invokes +.Xr strip 1 +directly. +This flag implies +.Fl s . +.It Fl s +.Nm +exec's the command +.Xr strip 1 +to strip binaries so that install can be portable over a large +number of systems and binary types. +If the environment variable +.Ev STRIP +is set, it is used as the +.Xr strip 1 +program. +.It Fl T Ar tags +Specify the +.Xr mtree 8 +tags to write out for the file when using +.Fl M Ar metalog . +.It Fl U +Indicate that install is running unprivileged, and that it should not +try to change the owner, the group, or the file flags of the destination. +The information that would have been updated can be stored in a log +file with +.Fl M Ar metalog . +.El +.Pp +By default, +.Nm +preserves all file flags, with the exception of the ``nodump'' flag. +.Pp +The +.Nm +utility attempts to prevent copying a file onto itself. +.Pp +Installing +.Pa /dev/null +creates an empty file. +.Sh ENVIRONMENT +.Bl -tag -width Fl +.It Ev STRIP +The program used to strip installed binaries when the +.Fl s +option is used. +If unspecified, +.Pa /usr/bin/strip +is used. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr mv 1 , +.Xr strip 1 , +.Xr chown 8 , +.Xr mtree 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . diff --git a/usr.bin/xinstall/pathnames.h b/usr.bin/xinstall/pathnames.h new file mode 100644 index 000000000..88ad1e25f --- /dev/null +++ b/usr.bin/xinstall/pathnames.h @@ -0,0 +1,36 @@ +/* $NetBSD: pathnames.h,v 1.6 2003/08/07 11:17:50 agc Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#ifndef _PATH_STRIP +#define _PATH_STRIP "/usr/bin/strip" +#endif diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c new file mode 100644 index 000000000..c9555c03c --- /dev/null +++ b/usr.bin/xinstall/xinstall.c @@ -0,0 +1,1287 @@ +/* $NetBSD: xinstall.c,v 1.115 2011/09/06 18:50:32 joerg Exp $ */ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_FUTIMES 1 +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#endif + +#include +#if defined(__COPYRIGHT) && !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1987, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; +#else +__RCSID("$NetBSD: xinstall.c,v 1.115 2011/09/06 18:50:32 joerg Exp $"); +#endif +#endif /* not lint */ + +#define __MKTEMP_OK__ /* All uses of mktemp have been checked */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pathnames.h" +#include "mtree.h" + +#define STRIP_ARGS_MAX 32 +#define BACKUP_SUFFIX ".old" + +static int dobackup, dodir, dostrip, dolink, dopreserve, dorename, dounpriv; +static int haveopt_f, haveopt_g, haveopt_m, haveopt_o; +static int numberedbackup; +static int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; +static char pathbuf[MAXPATHLEN]; +static uid_t uid = -1; +static gid_t gid = -1; +static char *group, *owner, *fflags, *tags; +static FILE *metafp; +static char *metafile; +static u_long fileflags; +static char *stripArgs; +static char *afterinstallcmd; +static const char *suffix = BACKUP_SUFFIX; +static char *destdir; + +enum { + DIGEST_NONE = 0, + DIGEST_MD5, + DIGEST_RMD160, + DIGEST_SHA1, + DIGEST_SHA256, + DIGEST_SHA384, + DIGEST_SHA512, +} digesttype = DIGEST_NONE; + +static char *digest; + +#define LN_ABSOLUTE 0x01 +#define LN_RELATIVE 0x02 +#define LN_HARD 0x04 +#define LN_SYMBOLIC 0x08 +#define LN_MIXED 0x10 + +#define DIRECTORY 0x01 /* Tell install it's a directory. */ +#define SETFLAGS 0x02 /* Tell install to set flags. */ +#define HASUID 0x04 /* Tell install the uid was given */ +#define HASGID 0x08 /* Tell install the gid was given */ + +static void afterinstall(const char *, const char *, int); +static void backup(const char *); +static char *copy(int, char *, int, char *, off_t); +static int do_link(char *, char *); +static void do_symlink(char *, char *); +static void install(char *, char *, u_int); +static void install_dir(char *, u_int); +static void makelink(char *, char *); +static void metadata_log(const char *, const char *, struct timeval *, + const char *, const char *, off_t); +static int parseid(char *, id_t *); +static void strip(char *); +__dead static void usage(void); +static char *xbasename(char *); +static char *xdirname(char *); + +int +main(int argc, char *argv[]) +{ + struct stat from_sb, to_sb; + void *set; + u_int iflags; + int ch, no_target; + char *p, *to_name; + + setprogname(argv[0]); + + iflags = 0; + while ((ch = getopt(argc, argv, "a:cbB:dD:f:g:h:l:m:M:N:o:prsS:T:U")) + != -1) + switch((char)ch) { + case 'a': + afterinstallcmd = strdup(optarg); + if (afterinstallcmd == NULL) + errx(1, "%s", strerror(ENOMEM)); + break; + case 'B': + suffix = optarg; + numberedbackup = 0; + { + /* Check if given suffix really generates + different suffixes - catch e.g. ".%" */ + char suffix_expanded0[FILENAME_MAX], + suffix_expanded1[FILENAME_MAX]; + (void)snprintf(suffix_expanded0, FILENAME_MAX, + suffix, 0); + (void)snprintf(suffix_expanded1, FILENAME_MAX, + suffix, 1); + if (strcmp(suffix_expanded0, suffix_expanded1) + != 0) + numberedbackup = 1; + } + /* fall through; -B implies -b */ + /*FALLTHROUGH*/ + case 'b': + dobackup = 1; + break; + case 'c': + /* ignored; was "docopy" which is now the default. */ + break; + case 'd': + dodir = 1; + break; + case 'D': + destdir = optarg; + break; +#if ! HAVE_NBTOOL_CONFIG_H + case 'f': + haveopt_f = 1; + fflags = optarg; + break; +#endif + case 'g': + haveopt_g = 1; + group = optarg; + break; + case 'h': + digest = optarg; + break; + case 'l': + for (p = optarg; *p; p++) + switch (*p) { + case 's': + dolink &= ~(LN_HARD|LN_MIXED); + dolink |= LN_SYMBOLIC; + break; + case 'h': + dolink &= ~(LN_SYMBOLIC|LN_MIXED); + dolink |= LN_HARD; + break; + case 'm': + dolink &= ~(LN_SYMBOLIC|LN_HARD); + dolink |= LN_MIXED; + break; + case 'a': + dolink &= ~LN_RELATIVE; + dolink |= LN_ABSOLUTE; + break; + case 'r': + dolink &= ~LN_ABSOLUTE; + dolink |= LN_RELATIVE; + break; + default: + errx(1, "%c: invalid link type", *p); + /* NOTREACHED */ + } + break; + case 'm': + haveopt_m = 1; + if (!(set = setmode(optarg))) + err(1, "Cannot set file mode `%s'", optarg); + mode = getmode(set, 0); + free(set); + break; + case 'M': + metafile = optarg; + break; + case 'N': + if (! setup_getid(optarg)) + errx(1, + "Unable to use user and group databases in `%s'", + optarg); + break; + case 'o': + haveopt_o = 1; + owner = optarg; + break; + case 'p': + dopreserve = 1; + errx(1, + "Minix lacks support for futimes(3)/utimes(2)"); + break; + case 'r': + dorename = 1; + break; + case 'S': + stripArgs = strdup(optarg); + if (stripArgs == NULL) + errx(1, "%s", strerror(ENOMEM)); + /* fall through; -S implies -s */ + /*FALLTHROUGH*/ + case 's': + dostrip = 1; + break; + case 'T': + tags = optarg; + break; + case 'U': + dounpriv = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* strip and link options make no sense when creating directories */ + if ((dostrip || dolink) && dodir) + usage(); + + /* strip and flags make no sense with links */ + if ((dostrip || fflags) && dolink) + usage(); + + /* must have at least two arguments, except when creating directories */ + if (argc < 2 && !dodir) + usage(); + + if (digest) { + if (0) { + } else if (strcmp(digest, "none") == 0) { + digesttype = DIGEST_NONE; + } else if (strcmp(digest, "md5") == 0) { + digesttype = DIGEST_MD5; + } else if (strcmp(digest, "rmd160") == 0) { + digesttype = DIGEST_RMD160; + } else if (strcmp(digest, "sha1") == 0) { + digesttype = DIGEST_SHA1; + } else if (strcmp(digest, "sha256") == 0) { + digesttype = DIGEST_SHA256; + } else if (strcmp(digest, "sha384") == 0) { + digesttype = DIGEST_SHA384; + } else if (strcmp(digest, "sha512") == 0) { + digesttype = DIGEST_SHA512; + } else { + warnx("unknown digest `%s'", digest); + usage(); + } + } + + /* get group and owner id's */ + if (group && !dounpriv) { + if (gid_from_group(group, &gid) == -1) { + id_t id; + if (!parseid(group, &id)) + errx(1, "unknown group %s", group); + gid = id; + } + iflags |= HASGID; + } + if (owner && !dounpriv) { + if (uid_from_user(owner, &uid) == -1) { + id_t id; + if (!parseid(owner, &id)) + errx(1, "unknown user %s", owner); + uid = id; + } + iflags |= HASUID; + } + +#if ! HAVE_NBTOOL_CONFIG_H + if (fflags && !dounpriv) { + if (string_to_flags(&fflags, &fileflags, NULL)) + errx(1, "%s: invalid flag", fflags); + /* restore fflags since string_to_flags() changed it */ + fflags = flags_to_string(fileflags, "-"); + iflags |= SETFLAGS; + } +#endif + + if (metafile) { + if ((metafp = fopen(metafile, "a")) == NULL) + warn("open %s", metafile); + } else + digesttype = DIGEST_NONE; + + if (dodir) { + for (; *argv != NULL; ++argv) + install_dir(*argv, iflags); + exit (0); + } + + no_target = stat(to_name = argv[argc - 1], &to_sb); + if (!no_target && S_ISDIR(to_sb.st_mode)) { + for (; *argv != to_name; ++argv) + install(*argv, to_name, iflags | DIRECTORY); + exit(0); + } + + /* can't do file1 file2 directory/file */ + if (argc != 2) { + errx(EXIT_FAILURE, "the last argument (%s) " + "must name an existing directory", argv[argc - 1]); + /* NOTREACHED */ + } + + if (!no_target) { + /* makelink() handles checks for links */ + if (!dolink) { + if (stat(*argv, &from_sb)) + err(1, "%s: stat", *argv); + if (!S_ISREG(to_sb.st_mode)) + errx(1, "%s: not a regular file", to_name); + if (to_sb.st_dev == from_sb.st_dev && + to_sb.st_ino == from_sb.st_ino) + errx(1, "%s and %s are the same file", *argv, + to_name); + } + /* + * Unlink now... avoid ETXTBSY errors later. Try and turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ +#ifndef __minix +#if ! HAVE_NBTOOL_CONFIG_H +#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) + if (to_sb.st_flags & NOCHANGEBITS) + (void)chflags(to_name, + to_sb.st_flags & ~(NOCHANGEBITS)); +#endif +#endif + if (dobackup) + backup(to_name); + else if (!dorename) + (void)unlink(to_name); + } + install(*argv, to_name, iflags); + exit(0); +} + +/* + * parseid -- + * parse uid or gid from arg into id, returning non-zero if successful + */ +static int +parseid(char *name, id_t *id) +{ + char *ep; + + errno = 0; + *id = (id_t)strtoul(name, &ep, 10); + if (errno || *ep != '\0') + return (0); + return (1); +} + +/* + * do_link -- + * make a hard link, obeying dorename if set + * return -1 on failure + */ +static int +do_link(char *from_name, char *to_name) +{ + char tmpl[MAXPATHLEN]; + int ret; + + if (dorename) { + (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); + /* This usage is safe. */ + if (mktemp(tmpl) == NULL) + err(1, "%s: mktemp", tmpl); + ret = link(from_name, tmpl); + if (ret == 0) { + ret = rename(tmpl, to_name); + /* If rename has posix semantics, then the temporary + * file may still exist when from_name and to_name point + * to the same file, so unlink it unconditionally. + */ + (void)unlink(tmpl); + } + return (ret); + } else + return (link(from_name, to_name)); +} + +/* + * do_symlink -- + * make a symbolic link, obeying dorename if set + * exit on failure + */ +static void +do_symlink(char *from_name, char *to_name) +{ + char tmpl[MAXPATHLEN]; + + if (dorename) { + (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); + /* This usage is safe. */ + if (mktemp(tmpl) == NULL) + err(1, "%s: mktemp", tmpl); + + if (symlink(from_name, tmpl) == -1) + err(1, "symlink %s -> %s", from_name, tmpl); + if (rename(tmpl, to_name) == -1) { + /* remove temporary link before exiting */ + (void)unlink(tmpl); + err(1, "%s: rename", to_name); + } + } else { + if (symlink(from_name, to_name) == -1) + err(1, "symlink %s -> %s", from_name, to_name); + } +} + +/* + * makelink -- + * make a link from source to destination + */ +static void +makelink(char *from_name, char *to_name) +{ + char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; + struct stat to_sb; + + /* Try hard links first */ + if (dolink & (LN_HARD|LN_MIXED)) { + if (do_link(from_name, to_name) == -1) { + if ((dolink & LN_HARD) || errno != EXDEV) + err(1, "link %s -> %s", from_name, to_name); + } else { + if (stat(to_name, &to_sb)) + err(1, "%s: stat", to_name); + if (S_ISREG(to_sb.st_mode)) { + /* XXX: hard links to anything + * other than plain files are not + * metalogged + */ + int omode; + char *oowner, *ogroup, *offlags; + char *dres; + + /* XXX: use underlying perms, + * unless overridden on command line. + */ + omode = mode; + if (!haveopt_m) + mode = (to_sb.st_mode & 0777); + oowner = owner; + if (!haveopt_o) + owner = NULL; + ogroup = group; + if (!haveopt_g) + group = NULL; + offlags = fflags; + if (!haveopt_f) + fflags = NULL; + switch (digesttype) { + case DIGEST_MD5: + dres = MD5File(from_name, NULL); + break; + case DIGEST_RMD160: + dres = RMD160File(from_name, NULL); + break; + case DIGEST_SHA1: + dres = SHA1File(from_name, NULL); + break; + case DIGEST_SHA256: + dres = SHA256_File(from_name, NULL); + break; + case DIGEST_SHA384: + dres = SHA384_File(from_name, NULL); + break; + case DIGEST_SHA512: + dres = SHA512_File(from_name, NULL); + break; + default: + dres = NULL; + } + metadata_log(to_name, "file", NULL, NULL, + dres, to_sb.st_size); + free(dres); + mode = omode; + owner = oowner; + group = ogroup; + fflags = offlags; + } + return; + } + } + + /* Symbolic links */ + if (dolink & LN_ABSOLUTE) { + /* Convert source path to absolute */ + if (realpath(from_name, src) == NULL) + err(1, "%s: realpath", from_name); + do_symlink(src, to_name); + /* XXX: src may point outside of destdir */ + metadata_log(to_name, "link", NULL, src, NULL, 0); + return; + } + + if (dolink & LN_RELATIVE) { + char *cp, *d, *s; + + /* Resolve pathnames */ + if (realpath(from_name, src) == NULL) + err(1, "%s: realpath", from_name); + + /* + * The last component of to_name may be a symlink, + * so use realpath to resolve only the directory. + */ + cp = xdirname(to_name); + if (realpath(cp, dst) == NULL) + err(1, "%s: realpath", cp); + /* .. and add the last component */ + if (strcmp(dst, "/") != 0) { + if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst)) + errx(1, "resolved pathname too long"); + } + cp = xbasename(to_name); + if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst)) + errx(1, "resolved pathname too long"); + + /* trim common path components */ + for (s = src, d = dst; *s == *d; s++, d++) + continue; + while (*s != '/') + s--, d--; + + /* count the number of directories we need to backtrack */ + for (++d, lnk[0] = '\0'; *d; d++) + if (*d == '/') + (void)strlcat(lnk, "../", sizeof(lnk)); + + (void)strlcat(lnk, ++s, sizeof(lnk)); + + do_symlink(lnk, to_name); + /* XXX: lnk may point outside of destdir */ + metadata_log(to_name, "link", NULL, lnk, NULL, 0); + return; + } + + /* + * If absolute or relative was not specified, + * try the names the user provided + */ + do_symlink(from_name, to_name); + /* XXX: from_name may point outside of destdir */ + metadata_log(to_name, "link", NULL, from_name, NULL, 0); +} + +/* + * install -- + * build a path name and install the file + */ +static void +install(char *from_name, char *to_name, u_int flags) +{ + struct stat from_sb; + struct stat to_sb; + struct timeval tv[2]; + off_t size; + int devnull, from_fd, to_fd, serrno, tmpmode; + char *p, tmpl[MAXPATHLEN], *oto_name, *digestresult; + + size = -1; + if (!dolink) { + /* ensure that from_sb & tv are sane if !dolink */ + if (stat(from_name, &from_sb)) + err(1, "%s: stat", from_name); + size = from_sb.st_size; +#if BSD4_4 && !HAVE_NBTOOL_CONFIG_H + TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec); +#else + tv[0].tv_sec = from_sb.st_atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = from_sb.st_mtime; + tv[1].tv_usec = 0; +#endif + } + + if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL) != 0) { + devnull = 0; + if (!dolink) { + if (!S_ISREG(from_sb.st_mode)) + errx(1, "%s: not a regular file", from_name); + } + /* Build the target path. */ + if (flags & DIRECTORY) { + (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", + to_name, + (p = strrchr(from_name, '/')) ? ++p : from_name); + to_name = pathbuf; + } + } else { + devnull = 1; + size = 0; +#if HAVE_STRUCT_STAT_ST_FLAGS + from_sb.st_flags = 0; /* XXX */ +#endif + } + + /* + * Unlink now... avoid ETXTBSY errors later. Try and turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ +#ifndef __minix +#if ! HAVE_NBTOOL_CONFIG_H + if (stat(to_name, &to_sb) == 0 && + to_sb.st_flags & (NOCHANGEBITS)) + (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); +#endif +#endif + if (dorename) { + (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); + oto_name = to_name; + to_name = tmpl; + } else { + oto_name = NULL; /* pacify gcc */ + if (dobackup) + backup(to_name); + else + (void)unlink(to_name); + } + + if (dolink) { + makelink(from_name, dorename ? oto_name : to_name); + return; + } + + /* Create target. */ + if (dorename) { + if ((to_fd = mkstemp(to_name)) == -1) + err(1, "%s: mkstemp", to_name); + } else { + if ((to_fd = open(to_name, + O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) + err(1, "%s: open", to_name); + } + digestresult = NULL; + if (!devnull) { + if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { + (void)unlink(to_name); + err(1, "%s: open", from_name); + } + digestresult = + copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); + (void)close(from_fd); + } + + if (dostrip) { + strip(to_name); + + /* + * Re-open our fd on the target, in case we used a strip + * that does not work in-place -- like gnu binutils strip. + */ + close(to_fd); + if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0) + err(1, "stripping %s", to_name); + + /* + * Recalculate size and digestresult after stripping. + */ + if (fstat(to_fd, &to_sb) != 0) + err(1, "%s: fstat", to_name); + size = to_sb.st_size; + digestresult = + copy(to_fd, to_name, -1, NULL, size); + + } + + if (afterinstallcmd != NULL) { + afterinstall(afterinstallcmd, to_name, 1); + + /* + * Re-open our fd on the target, in case we used an + * after-install command that does not work in-place + */ + close(to_fd); + if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0) + err(1, "running after install command on %s", to_name); + } + + /* + * Set owner, group, mode for target; do the chown first, + * chown may lose the setuid bits. + */ + if (!dounpriv && + (flags & (HASUID | HASGID)) && fchown(to_fd, uid, gid) == -1) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno)); + } + tmpmode = mode; + if (dounpriv) + tmpmode &= S_IRWXU|S_IRWXG|S_IRWXO; + if (fchmod(to_fd, tmpmode) == -1) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chmod: %s", to_name, strerror(serrno)); + } + + /* + * Preserve the date of the source file. + */ +#ifndef __minix + if (dopreserve) { +#if HAVE_FUTIMES + if (futimes(to_fd, tv) == -1) + warn("%s: futimes", to_name); +#else + if (utimes(to_name, tv) == -1) + warn("%s: utimes", to_name); +#endif + } +#endif + + (void)close(to_fd); + + if (dorename) { + if (rename(to_name, oto_name) == -1) + err(1, "%s: rename", to_name); + to_name = oto_name; + } + + /* + * If provided a set of flags, set them, otherwise, preserve the + * flags, except for the dump flag. + */ +#ifndef __minix +#if ! HAVE_NBTOOL_CONFIG_H + if (!dounpriv && chflags(to_name, + flags & SETFLAGS ? fileflags : from_sb.st_flags & ~UF_NODUMP) == -1) + { + if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) + warn("%s: chflags", to_name); + } +#endif +#endif + + metadata_log(to_name, "file", tv, NULL, digestresult, size); + free(digestresult); +} + +/* + * copy -- + * copy from one file to another, returning a digest. + * + * If to_fd < 0, just calculate a digest, don't copy. + */ +static char * +copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size) +{ + ssize_t nr, nw; + int serrno; + u_char *p; + u_char buf[MAXBSIZE]; + MD5_CTX ctxMD5; + RMD160_CTX ctxRMD160; + SHA1_CTX ctxSHA1; + SHA256_CTX ctxSHA256; + SHA384_CTX ctxSHA384; + SHA512_CTX ctxSHA512; + + switch (digesttype) { + case DIGEST_MD5: + MD5Init(&ctxMD5); + break; + case DIGEST_RMD160: + RMD160Init(&ctxRMD160); + break; + case DIGEST_SHA1: + SHA1Init(&ctxSHA1); + break; + case DIGEST_SHA256: + SHA256_Init(&ctxSHA256); + break; + case DIGEST_SHA384: + SHA384_Init(&ctxSHA384); + break; + case DIGEST_SHA512: + SHA512_Init(&ctxSHA512); + break; + case DIGEST_NONE: + if (to_fd < 0) + return NULL; /* no need to do anything */ + default: + break; + } + /* + * There's no reason to do anything other than close the file + * now if it's empty, so let's not bother. + */ + if (size > 0) { + + /* + * Mmap and write if less than 8M (the limit is so we + * don't totally trash memory on big files). This is + * really a minor hack, but it wins some CPU back. + */ + + if (size <= 8 * 1048576) { +#ifdef __minix + goto mmap_failed; +#else + if ((p = mmap(NULL, (size_t)size, PROT_READ, + MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) + == MAP_FAILED) { + goto mmap_failed; + } +#if defined(MADV_SEQUENTIAL) && !defined(__APPLE__) + if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1 + && errno != EOPNOTSUPP) + warnx("madvise: %s", strerror(errno)); +#endif + + if (to_fd >= 0 && write(to_fd, p, size) != size) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: write: %s", + to_name, strerror(serrno)); + } + switch (digesttype) { + case DIGEST_MD5: + MD5Update(&ctxMD5, p, size); + break; + case DIGEST_RMD160: + RMD160Update(&ctxRMD160, p, size); + break; + case DIGEST_SHA1: + SHA1Update(&ctxSHA1, p, size); + break; + case DIGEST_SHA256: + SHA256_Update(&ctxSHA256, p, size); + break; + case DIGEST_SHA384: + SHA384_Update(&ctxSHA384, p, size); + break; + case DIGEST_SHA512: + SHA512_Update(&ctxSHA512, p, size); + break; + default: + break; + } + (void)munmap(p, size); +#endif + } else { + mmap_failed: + while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { + if (to_fd >= 0 && + (nw = write(to_fd, buf, nr)) != nr) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: write: %s", to_name, + strerror(nw > 0 ? EIO : serrno)); + } + switch (digesttype) { + case DIGEST_MD5: + MD5Update(&ctxMD5, buf, nr); + break; + case DIGEST_RMD160: + RMD160Update(&ctxRMD160, buf, nr); + break; + case DIGEST_SHA1: + SHA1Update(&ctxSHA1, buf, nr); + break; + case DIGEST_SHA256: + SHA256_Update(&ctxSHA256, buf, nr); + break; + case DIGEST_SHA384: + SHA384_Update(&ctxSHA384, buf, nr); + break; + case DIGEST_SHA512: + SHA512_Update(&ctxSHA512, buf, nr); + break; + default: + break; + } + } + if (nr != 0) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: read: %s", from_name, strerror(serrno)); + } + } + } + switch (digesttype) { + case DIGEST_MD5: + return MD5End(&ctxMD5, NULL); + case DIGEST_RMD160: + return RMD160End(&ctxRMD160, NULL); + case DIGEST_SHA1: + return SHA1End(&ctxSHA1, NULL); + case DIGEST_SHA256: + return SHA256_End(&ctxSHA256, NULL); + case DIGEST_SHA384: + return SHA384_End(&ctxSHA384, NULL); + case DIGEST_SHA512: + return SHA512_End(&ctxSHA512, NULL); + default: + return NULL; + } +} + +/* + * strip -- + * use strip(1) to strip the target file + */ +static void +strip(char *to_name) +{ + static const char exec_failure[] = ": exec of strip failed: "; + int serrno, status; + const char * volatile stripprog, *progname; + char *cmd; + + if ((stripprog = getenv("STRIP")) == NULL || *stripprog == '\0') { +#ifdef TARGET_STRIP + stripprog = TARGET_STRIP; +#else + stripprog = _PATH_STRIP; +#endif + } + + cmd = NULL; + + if (stripArgs) { + /* + * Build up a command line and let /bin/sh + * parse the arguments. + */ + int ret = asprintf(&cmd, "%s %s %s", stripprog, stripArgs, + to_name); + + if (ret == -1 || cmd == NULL) + err(1, "asprintf failed"); + } + + switch (vfork()) { + case -1: + serrno = errno; + (void)unlink(to_name); + errx(1, "vfork: %s", strerror(serrno)); + /*NOTREACHED*/ + case 0: + + if (stripArgs) + execl(_PATH_BSHELL, "sh", "-c", cmd, NULL); + else + execlp(stripprog, "strip", to_name, NULL); + + progname = getprogname(); + write(STDERR_FILENO, progname, strlen(progname)); + write(STDERR_FILENO, exec_failure, strlen(exec_failure)); + write(STDERR_FILENO, stripprog, strlen(stripprog)); + write(STDERR_FILENO, "\n", 1); + _exit(1); + /*NOTREACHED*/ + default: + if (wait(&status) == -1 || status) + (void)unlink(to_name); + } + + free(cmd); +} + +/* + * afterinstall -- + * run provided command on the target file or directory after it's been + * installed and stripped, but before permissions are set or it's renamed + */ +static void +afterinstall(const char *command, const char *to_name, int errunlink) +{ + int serrno, status; + char *cmd; + + switch (vfork()) { + case -1: + serrno = errno; + if (errunlink) + (void)unlink(to_name); + errx(1, "vfork: %s", strerror(serrno)); + /*NOTREACHED*/ + case 0: + /* + * build up a command line and let /bin/sh + * parse the arguments + */ + cmd = (char*)malloc(sizeof(char)* + (2+strlen(command)+ + strlen(to_name))); + + if (cmd == NULL) + errx(1, "%s", strerror(ENOMEM)); + + sprintf(cmd, "%s %s", command, to_name); + + execl(_PATH_BSHELL, "sh", "-c", cmd, NULL); + + warn("%s: exec of after install command", command); + _exit(1); + /*NOTREACHED*/ + default: + if ((wait(&status) == -1 || status) && errunlink) + (void)unlink(to_name); + } +} + +/* + * backup -- + * backup file "to_name" to to_name.suffix + * if suffix contains a "%", it's taken as a printf(3) pattern + * used for a numbered backup. + */ +static void +backup(const char *to_name) +{ + char bname[FILENAME_MAX]; + + if (numberedbackup) { + /* Do numbered backup */ + int cnt; + char suffix_expanded[FILENAME_MAX]; + + cnt=0; + do { + (void)snprintf(suffix_expanded, FILENAME_MAX, suffix, + cnt); + (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, + suffix_expanded); + cnt++; + } while (access(bname, F_OK) == 0); + } else { + /* Do simple backup */ + (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, suffix); + } + + (void)rename(to_name, bname); +} + +/* + * install_dir -- + * build directory hierarchy + */ +static void +install_dir(char *path, u_int flags) +{ + char *p; + struct stat sb; + int ch; + + for (p = path;; ++p) + if (!*p || (p != path && *p == '/')) { + ch = *p; + *p = '\0'; + if (mkdir(path, 0777) < 0) { + /* + * Can't create; path exists or no perms. + * stat() path to determine what's there now. + */ + int sverrno; + sverrno = errno; + if (stat(path, &sb) < 0) { + /* Not there; use mkdir()s error */ + errno = sverrno; + err(1, "%s: mkdir", path); + } + if (!S_ISDIR(sb.st_mode)) { + errx(1, + "%s exists but is not a directory", + path); + } + } + if (!(*p = ch)) + break; + } + + if (afterinstallcmd != NULL) + afterinstall(afterinstallcmd, path, 0); + + if (!dounpriv && ( + ((flags & (HASUID | HASGID)) && chown(path, uid, gid) == -1) + || chmod(path, mode) == -1 )) { + warn("%s: chown/chmod", path); + } + metadata_log(path, "dir", NULL, NULL, NULL, 0); +} + +/* + * metadata_log -- + * if metafp is not NULL, output mtree(8) full path name and settings to + * metafp, to allow permissions to be set correctly by other tools, + * or to allow integrity checks to be performed. + */ +static void +metadata_log(const char *path, const char *type, struct timeval *tv, + const char *slink, const char *digestresult, off_t size) +{ + static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; + const char *p; + char *buf; + size_t destlen; + struct flock metalog_lock; + + if (!metafp) + return; + buf = (char *)malloc(4 * strlen(path) + 1); /* buf for strsvis(3) */ + if (buf == NULL) { + warnx("%s", strerror(ENOMEM)); + return; + } + /* lock log file */ + metalog_lock.l_start = 0; + metalog_lock.l_len = 0; + metalog_lock.l_whence = SEEK_SET; + metalog_lock.l_type = F_WRLCK; + if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) { + warn("can't lock %s", metafile); + free(buf); + return; + } + + p = path; /* remove destdir */ + if (destdir) { + destlen = strlen(destdir); + if (strncmp(p, destdir, destlen) == 0 && + (p[destlen] == '/' || p[destlen] == '\0')) + p += destlen; + } + while (*p && *p == '/') /* remove leading /s */ + p++; + strsvis(buf, p, VIS_CSTYLE, extra); /* encode name */ + p = buf; + /* print details */ + fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type); + if (owner) + fprintf(metafp, " uname=%s", owner); + if (group) + fprintf(metafp, " gname=%s", group); + fprintf(metafp, " mode=%#o", mode); + if (slink) { + strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */ + fprintf(metafp, " link=%s", buf); + } + if (*type == 'f') /* type=file */ + fprintf(metafp, " size=%lld", (long long)size); + if (tv != NULL && dopreserve) + fprintf(metafp, " time=%lld.%ld", + (long long)tv[1].tv_sec, (long)tv[1].tv_usec); + if (digestresult && digest) + fprintf(metafp, " %s=%s", digest, digestresult); + if (fflags) + fprintf(metafp, " flags=%s", fflags); + if (tags) + fprintf(metafp, " tags=%s", tags); + fputc('\n', metafp); + fflush(metafp); /* flush output */ + /* unlock log file */ + metalog_lock.l_type = F_UNLCK; + if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) { + warn("can't unlock %s", metafile); + } + free(buf); +} + +/* + * xbasename -- + * libc basename(3) that returns a pointer to a static buffer + * instead of overwriting that passed-in string. + */ +static char * +xbasename(char *path) +{ + static char tmp[MAXPATHLEN]; + + (void)strlcpy(tmp, path, sizeof(tmp)); + return (basename(tmp)); +} + +/* + * xdirname -- + * libc dirname(3) that returns a pointer to a static buffer + * instead of overwriting that passed-in string. + */ +static char * +xdirname(char *path) +{ + static char tmp[MAXPATHLEN]; + + (void)strlcpy(tmp, path, sizeof(tmp)); + return (dirname(tmp)); +} + +/* + * usage -- + * print a usage message and die + */ +static void +usage(void) +{ + const char *prog; + + prog = getprogname(); + + (void)fprintf(stderr, +"usage: %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n" +" [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group] \n" +" [-l linkflags] [-h hash] [-S stripflags] file1 file2\n" +" %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n" +" [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group]\n" +" [-l linkflags] [-h hash] [-S stripflags] file1 ... fileN directory\n" +" %s -d [-Up] [-M log] [-D dest] [-T tags] [-a aftercmd] [-m mode]\n" +" [-N dbdir] [-o owner] [-g group] directory ...\n", + prog, prog, prog); + exit(1); +}