git: 06a6d0259fe5 - stable/14 - Revert "tzsetup: symlink /etc/localtime instead of copying"

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Sat, 21 Sep 2024 11:10:34 UTC
The branch stable/14 has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=06a6d0259fe5721bfb3b745b231ecf7cf3a5e28e

commit 06a6d0259fe5721bfb3b745b231ecf7cf3a5e28e
Author:     Ed Maste <emaste@FreeBSD.org>
AuthorDate: 2024-09-21 11:06:02 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2024-09-21 11:06:39 +0000

    Revert "tzsetup: symlink /etc/localtime instead of copying"
    
    This failed when used with tzsetup's -C option (for example, when using
    etcupdate -D to update a jail from the host).  Revert the stable/14 MFC
    for now; will be reapplied after being fixed in main..
    
    This reverts commit fc43a1b6842afa806dfd7ba48de5bece63d04456.
    This reverts commit 87f7f0389f8b7bf30ef12df5c0d337cb2789883e.
---
 usr.sbin/etcupdate/tests/tzsetup_test.sh |   5 +-
 usr.sbin/tzsetup/tzsetup.8               |   2 +-
 usr.sbin/tzsetup/tzsetup.c               | 119 ++++++++++++++++++++++++-------
 3 files changed, 96 insertions(+), 30 deletions(-)

diff --git a/usr.sbin/etcupdate/tests/tzsetup_test.sh b/usr.sbin/etcupdate/tests/tzsetup_test.sh
index 155830bddae7..dd76884e13eb 100644
--- a/usr.sbin/etcupdate/tests/tzsetup_test.sh
+++ b/usr.sbin/etcupdate/tests/tzsetup_test.sh
@@ -232,8 +232,7 @@ echo "Differences for real update:"
 diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/test.out \
     || FAILED=yes
 
-# XXX tzsetup installs a symlink as of 5e16809c953f
-#file /etc/localtime "foo"
-#file /var/db/zoneinfo "foo"
+file /etc/localtime "foo"
+file /var/db/zoneinfo "foo"
 
 [ "${FAILED}" = no ]
diff --git a/usr.sbin/tzsetup/tzsetup.8 b/usr.sbin/tzsetup/tzsetup.8
index 499d25765541..bfa625a1af3a 100644
--- a/usr.sbin/tzsetup/tzsetup.8
+++ b/usr.sbin/tzsetup/tzsetup.8
@@ -52,7 +52,7 @@ The following options are available:
 Open all files and directories relative to
 .Ar chroot_directory .
 .It Fl n
-Do not create or symlink files.
+Do not create or copy files.
 .It Fl r
 Reinstall the zoneinfo file installed last time.
 The name is obtained from
diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c
index 617de4efb765..6cd2e16b607c 100644
--- a/usr.sbin/tzsetup/tzsetup.c
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -744,42 +744,109 @@ static void message_zoneinfo_file(const char *title, char *prompt)
 static int
 install_zoneinfo_file(const char *zoneinfo_file)
 {
+	char		buf[1024];
 	char		prompt[SILLY_BUFFER_SIZE];
+	struct stat	sb;
+	ssize_t		len;
+	int		fd1, fd2, copymode;
+
+	if (lstat(path_localtime, &sb) < 0) {
+		/* Nothing there yet... */
+		copymode = 1;
+	} else if (S_ISLNK(sb.st_mode))
+		copymode = 0;
+	else
+		copymode = 1;
 
 #ifdef VERBOSE
-	snprintf(prompt, sizeof(prompt), "Creating symbolic link %s to %s",
-	    path_localtime, zoneinfo_file);
+	if (copymode)
+		snprintf(prompt, sizeof(prompt),
+		    "Copying %s to %s", zoneinfo_file, path_localtime);
+	else
+		snprintf(prompt, sizeof(prompt),
+		    "Creating symbolic link %s to %s",
+		    path_localtime, zoneinfo_file);
 	message_zoneinfo_file("Info", prompt);
 #endif
 
 	if (reallydoit) {
-		if (access(zoneinfo_file, R_OK) != 0) {
-			snprintf(prompt, sizeof(prompt),
-			    "Cannot access %s: %s", zoneinfo_file,
-			    strerror(errno));
-			message_zoneinfo_file("Error", prompt);
-			return (DITEM_FAILURE | DITEM_RECREATE);
-		}
-		if (unlink(path_localtime) < 0 && errno != ENOENT) {
-			snprintf(prompt, sizeof(prompt),
-			    "Could not delete %s: %s",
-			    path_localtime, strerror(errno));
-			message_zoneinfo_file("Error", prompt);
-			return (DITEM_FAILURE | DITEM_RECREATE);
-		}
-		if (symlink(zoneinfo_file, path_localtime) < 0) {
-			snprintf(prompt, sizeof(prompt),
-			    "Cannot create symbolic link %s to %s: %s",
-			    path_localtime, zoneinfo_file,
-			    strerror(errno));
-			message_zoneinfo_file("Error", prompt);
-			return (DITEM_FAILURE | DITEM_RECREATE);
+		if (copymode) {
+			fd1 = open(zoneinfo_file, O_RDONLY, 0);
+			if (fd1 < 0) {
+				snprintf(prompt, sizeof(prompt),
+				    "Could not open %s: %s", zoneinfo_file,
+				    strerror(errno));
+				message_zoneinfo_file("Error", prompt);
+				return (DITEM_FAILURE | DITEM_RECREATE);
+			}
+
+			if (unlink(path_localtime) < 0 && errno != ENOENT) {
+				snprintf(prompt, sizeof(prompt),
+				    "Could not delete %s: %s",
+				    path_localtime, strerror(errno));
+				message_zoneinfo_file("Error", prompt);
+				return (DITEM_FAILURE | DITEM_RECREATE);
+			}
+
+			fd2 = open(path_localtime, O_CREAT | O_EXCL | O_WRONLY,
+			    S_IRUSR | S_IRGRP | S_IROTH);
+			if (fd2 < 0) {
+				snprintf(prompt, sizeof(prompt),
+				    "Could not open %s: %s",
+				    path_localtime, strerror(errno));
+				message_zoneinfo_file("Error", prompt);
+				return (DITEM_FAILURE | DITEM_RECREATE);
+			}
+
+			while ((len = read(fd1, buf, sizeof(buf))) > 0)
+				if ((len = write(fd2, buf, len)) < 0)
+					break;
+
+			if (len == -1) {
+				snprintf(prompt, sizeof(prompt),
+				    "Error copying %s to %s %s", zoneinfo_file,
+				    path_localtime, strerror(errno));
+				message_zoneinfo_file("Error", prompt);
+				/* Better to leave none than a corrupt one. */
+				unlink(path_localtime);
+				return (DITEM_FAILURE | DITEM_RECREATE);
+			}
+			close(fd1);
+			close(fd2);
+		} else {
+			if (access(zoneinfo_file, R_OK) != 0) {
+				snprintf(prompt, sizeof(prompt),
+				    "Cannot access %s: %s", zoneinfo_file,
+				    strerror(errno));
+				message_zoneinfo_file("Error", prompt);
+				return (DITEM_FAILURE | DITEM_RECREATE);
+			}
+			if (unlink(path_localtime) < 0 && errno != ENOENT) {
+				snprintf(prompt, sizeof(prompt),
+				    "Could not delete %s: %s",
+				    path_localtime, strerror(errno));
+				message_zoneinfo_file("Error", prompt);
+				return (DITEM_FAILURE | DITEM_RECREATE);
+			}
+			if (symlink(zoneinfo_file, path_localtime) < 0) {
+				snprintf(prompt, sizeof(prompt),
+				    "Cannot create symbolic link %s to %s: %s",
+				    path_localtime, zoneinfo_file,
+				    strerror(errno));
+				message_zoneinfo_file("Error", prompt);
+				return (DITEM_FAILURE | DITEM_RECREATE);
+			}
 		}
 
 #ifdef VERBOSE
-		snprintf(prompt, sizeof(prompt),
-		    "Created symbolic link from %s to %s", zoneinfo_file,
-		    path_localtime);
+		if (copymode)
+			snprintf(prompt, sizeof(prompt),
+			    "Copied timezone file from %s to %s",
+			    zoneinfo_file, path_localtime);
+		else
+			snprintf(prompt, sizeof(prompt),
+			    "Created symbolic link from %s to %s",
+			    zoneinfo_file, path_localtime);
 		message_zoneinfo_file("Done", prompt);
 #endif
 	} /* reallydoit */