Index: head/usr.bin/xinstall/xinstall.c
===================================================================
--- head/usr.bin/xinstall/xinstall.c	(revision 192913)
+++ head/usr.bin/xinstall/xinstall.c	(working copy)
@@ -93,6 +93,7 @@
 void	install(const char *, const char *, u_long, u_int);
 void	install_dir(char *);
 u_long	numeric_id(const char *, const char *);
+void	set_attributes(int, const char *);
 void	strip(const char *);
 int	trymmap(int);
 void	usage(void);
@@ -353,6 +354,9 @@
 			     tempcopy ? tempfile : to_name, from_sb.st_size);
 	}
 
+	if (!devnull)
+		(void)close(from_fd);
+
 	if (dostrip) {
 		strip(tempcopy ? tempfile : to_name);
 
@@ -369,9 +373,8 @@
 	/*
 	 * Compare the stripped temp file with the target.
 	 */
+	temp_fd = to_fd;
 	if (docompare && dostrip && target) {
-		temp_fd = to_fd;
-
 		/* Re-open to_fd using the real target name. */
 		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
 			err(EX_OSERR, "%s", to_name);
@@ -400,7 +403,6 @@
 				files_match = 1;
 				(void)unlink(tempfile);
 			}
-			(void) close(temp_fd);
 		}
 	}
 
@@ -409,6 +411,12 @@
 	 * and the files are different (or just not compared).
 	 */
 	if (tempcopy && !files_match) {
+		/*
+		 * Set attributes before rename so we can safely abort
+		 * on failure and leave the target untouched.
+		 */
+		set_attributes(temp_fd, tempfile);
+
 		/* Try to turn off the immutable bits. */
 		if (to_sb.st_flags & NOCHANGEBITS)
 			(void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
@@ -441,10 +449,14 @@
 
 		/* Re-open to_fd so we aren't hosed by the rename(2). */
 		(void) close(to_fd);
-		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
-			err(EX_OSERR, "%s", to_name);
-	}
+		to_fd = temp_fd;
+		temp_fd = -1;
+	} else
+		set_attributes(to_fd, to_name);
 
+	if (temp_fd != to_fd)
+		(void)close(temp_fd);
+
 	/*
 	 * Preserve the timestamp of the source file if necessary.
 	 */
@@ -456,43 +468,7 @@
 		(void)utimes(to_name, tvb);
 	}
 
-	if (fstat(to_fd, &to_sb) == -1) {
-		serrno = errno;
-		(void)unlink(to_name);
-		errno = serrno;
-		err(EX_OSERR, "%s", to_name);
-	}
-
 	/*
-	 * Set owner, group, mode for target; do the chown first,
-	 * chown may lose the setuid bits.
-	 */
-	if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
-	    (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
-	    (mode != (to_sb.st_mode & ALLPERMS))) {
-		/* Try to turn off the immutable bits. */
-		if (to_sb.st_flags & NOCHANGEBITS)
-			(void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
-	}
-
-	if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
-	    (uid != (uid_t)-1 && uid != to_sb.st_uid))
-		if (fchown(to_fd, uid, gid) == -1) {
-			serrno = errno;
-			(void)unlink(to_name);
-			errno = serrno;
-			err(EX_OSERR,"%s: chown/chgrp", to_name);
-		}
-
-	if (mode != (to_sb.st_mode & ALLPERMS))
-		if (fchmod(to_fd, mode)) {
-			serrno = errno;
-			(void)unlink(to_name);
-			errno = serrno;
-			err(EX_OSERR, "%s: chmod", to_name);
-		}
-
-	/*
 	 * If provided a set of flags, set them, otherwise, preserve the
 	 * flags, except for the dump flag.
 	 * NFS does not support flags.  Ignore EOPNOTSUPP flags if we're just
@@ -508,7 +484,7 @@
 				warn("%s: chflags", to_name);
 			else {
 				serrno = errno;
-				(void)unlink(to_name);
+				(void)close(to_fd);
 				errno = serrno;
 				err(EX_OSERR, "%s: chflags", to_name);
 			}
@@ -516,8 +492,6 @@
 	}
 
 	(void)close(to_fd);
-	if (!devnull)
-		(void)close(from_fd);
 }
 
 /*
@@ -701,6 +675,54 @@
 }
 
 /*
+ * set_attributes --
+ *	set file mode, uid and gid
+ */
+void
+set_attributes(int fd, const char *filename)
+{
+	struct stat sb;
+	int serrno;
+
+	if (fstat(fd, &sb) == -1) {
+		serrno = errno;
+		(void)unlink(filename);
+		errno = serrno;
+		err(EX_OSERR, "%s", filename);
+	}
+
+	/*
+	 * Set owner, group, mode for target; do the chown first,
+	 * chown may lose the setuid bits.
+	 */
+	if ((gid != (gid_t)-1 && gid != sb.st_gid) ||
+	    (uid != (uid_t)-1 && uid != sb.st_uid) ||
+	    (mode != (sb.st_mode & ALLPERMS))) {
+		/* Try to turn off the immutable bits. */
+		if (sb.st_flags & NOCHANGEBITS)
+			(void)fchflags(fd, sb.st_flags & ~NOCHANGEBITS);
+	}
+
+	if ((gid != (gid_t)-1 && gid != sb.st_gid) ||
+	    (uid != (uid_t)-1 && uid != sb.st_uid))
+		if (fchown(fd, uid, gid) == -1) {
+			serrno = errno;
+			(void)unlink(filename);
+			errno = serrno;
+			err(EX_OSERR,"%s: chown/chgrp", filename);
+		}
+
+
+	if (mode != (sb.st_mode & ALLPERMS))
+		if (fchmod(fd, mode)) {
+			serrno = errno;
+			(void)unlink(filename);
+			errno = serrno;
+			err(EX_OSERR, "%s: chmod", filename);
+		}
+}
+
+/*
  * strip --
  *	use strip(1) to strip the target file
  */
