Module Name:    src
Committed By:   martin
Date:           Wed Aug  7 10:55:29 UTC 2024

Modified Files:
        src/bin/pax [netbsd-9]: ar_subs.c buf_subs.c extern.h file_subs.c
            ftree.c pax.c
        src/tests/bin/pax [netbsd-9]: t_pax.sh

Log Message:
Pull up following revision(s) (requested by riastradh in ticket #1863):

        bin/pax/buf_subs.c: revision 1.31
        tests/bin/pax/t_pax.sh: revision 1.3
        tests/bin/pax/t_pax.sh: revision 1.4
        tests/bin/pax/t_pax.sh: revision 1.5
        tests/bin/pax/t_pax.sh: revision 1.6
        tests/bin/pax/t_pax.sh: revision 1.7
        bin/pax/extern.h: revision 1.61
        bin/pax/pax.c: revision 1.50
        bin/pax/pax.c: revision 1.51
        bin/pax/file_subs.c: revision 1.65
        bin/pax/ftree.c: revision 1.43
        bin/pax/ar_subs.c: revision 1.58

pax: exit 0 if stdin filelist is used and empty

If copying a list of files from stdin, exit zero instead of non-zero
if there are no files supplied.

AFAICT, POSIX doesn't require a non-zero an error in this situation,
since there are no files to not match.

Fix from PR bin/41736 by Lloyd Parkes.

pax: don't overwrite destination if -r -w copy fails

Add more error handling to pax -r -w so that any failure
during the copy to the temporary file (including a failed flush)
prevents any existing destination file from being replaced
with the partial (including possibly empty) temporary file.

The partial temporary file is removed.  pax still exists non-zero.
Thanks to Michael van Elst (mlelstv@) for the analysis
of the problem in the PR.
Should fix PR misc/33753.

tests/bin/pax: Add test for pax list in an ungettable cwd.
The list operation has no need to touch the file system, so it should
have no need for open(".") or getcwd() to succeed.

PR bin/44498: tar(1) unnecessarily demands that getcwd() work
(The PR was filed about tar(1), which is now bsdtar by default, but
the issue applies to pax(1) too and would continue to apply to tar(1)
if you set MKBSDTAR=no.)
pax(1): Don't require open(".") or getcwd to work for list operation.

PR 44498: tar(1) unnecessarily demands that getcwd() work
tests/bin/pax: Test pax(1) succeeds with empty file list on stdin.

PR bin/41736: pax reports an error when copying zero files
tests/bin/pax: Add some more cwd-related test cases.

PR bin/44498: tar(1) unnecessarily demands that getcwd() work
pax(1): Only require getcwd if we're reading without --insecure.

No other paths use the result of getcwd.

PR bin/44498: tar(1) unnecessarily demands that getcwd() work


To generate a diff of this commit:
cvs rdiff -u -r1.56 -r1.56.46.1 src/bin/pax/ar_subs.c
cvs rdiff -u -r1.29 -r1.29.4.1 src/bin/pax/buf_subs.c
cvs rdiff -u -r1.59.36.1 -r1.59.36.2 src/bin/pax/extern.h
cvs rdiff -u -r1.64 -r1.64.2.1 src/bin/pax/file_subs.c
cvs rdiff -u -r1.42 -r1.42.34.1 src/bin/pax/ftree.c
cvs rdiff -u -r1.49 -r1.49.2.1 src/bin/pax/pax.c
cvs rdiff -u -r1.1 -r1.1.36.1 src/tests/bin/pax/t_pax.sh

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/pax/ar_subs.c
diff -u src/bin/pax/ar_subs.c:1.56 src/bin/pax/ar_subs.c:1.56.46.1
--- src/bin/pax/ar_subs.c:1.56	Wed Aug 31 16:24:54 2011
+++ src/bin/pax/ar_subs.c	Wed Aug  7 10:55:29 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: ar_subs.c,v 1.56 2011/08/31 16:24:54 plunky Exp $	*/
+/*	$NetBSD: ar_subs.c,v 1.56.46.1 2024/08/07 10:55:29 martin Exp $	*/
 
 /*-
  * Copyright (c) 1992 Keith Muller.
@@ -42,7 +42,7 @@
 #if 0
 static char sccsid[] = "@(#)ar_subs.c	8.2 (Berkeley) 4/18/94";
 #else
-__RCSID("$NetBSD: ar_subs.c,v 1.56 2011/08/31 16:24:54 plunky Exp $");
+__RCSID("$NetBSD: ar_subs.c,v 1.56.46.1 2024/08/07 10:55:29 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -1131,10 +1131,14 @@ copy(void)
 		}
 
 		/*
-		 * copy source file data to the destination file
+		 * copy source file data to the destination file.
+		 * if there was a failure, remove the temporary file
+		 * and leave any existing destination file unmodified.
 		 */
-		cp_file(arcn, fdsrc, fddest);
-		file_close(arcn, fddest);
+		if (cp_file(arcn, fdsrc, fddest) < 0)
+			file_cleanup(arcn, fddest);
+		else
+			file_close(arcn, fddest);
 		rdfile_close(arcn, &fdsrc);
 
 		if (vflag && vfpart) {

Index: src/bin/pax/buf_subs.c
diff -u src/bin/pax/buf_subs.c:1.29 src/bin/pax/buf_subs.c:1.29.4.1
--- src/bin/pax/buf_subs.c:1.29	Mon Mar 19 03:11:39 2018
+++ src/bin/pax/buf_subs.c	Wed Aug  7 10:55:29 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: buf_subs.c,v 1.29 2018/03/19 03:11:39 msaitoh Exp $	*/
+/*	$NetBSD: buf_subs.c,v 1.29.4.1 2024/08/07 10:55:29 martin Exp $	*/
 
 /*-
  * Copyright (c) 1992 Keith Muller.
@@ -42,7 +42,7 @@
 #if 0
 static char sccsid[] = "@(#)buf_subs.c	8.2 (Berkeley) 4/18/94";
 #else
-__RCSID("$NetBSD: buf_subs.c,v 1.29 2018/03/19 03:11:39 msaitoh Exp $");
+__RCSID("$NetBSD: buf_subs.c,v 1.29.4.1 2024/08/07 10:55:29 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -745,8 +745,11 @@ rd_wrfile(ARCHD *arcn, int ofd, off_t *l
 	 * written. just closing with the file offset moved forward may not put
 	 * a hole at the end of the file.
 	 */
-	if (ofd >= 0 && isem && (arcn->sb.st_size > 0L))
-		file_flush(ofd, fnm, isem);
+	if (ofd >= 0 && isem && (arcn->sb.st_size > 0L)) {
+		if (file_flush(ofd, fnm, isem) < 0) {
+			/* write flush errors are not an error here */;
+		}
+	}
 
 	/*
 	 * if we failed from archive read, we do not want to skip
@@ -758,9 +761,11 @@ rd_wrfile(ARCHD *arcn, int ofd, off_t *l
 	 * some formats record a crc on file data. If so, then we compare the
 	 * calculated crc to the crc stored in the archive
 	 */
-	if (docrc && (size == 0L) && (arcn->crc != crc))
+	if (docrc && (size == 0L) && (arcn->crc != crc)) {
 		tty_warn(1,"Actual crc does not match expected crc %s",
 		    arcn->name);
+		/* crc warning is not an error */
+	}
 	return 0;
 }
 
@@ -769,9 +774,11 @@ rd_wrfile(ARCHD *arcn, int ofd, off_t *l
  *	copy the contents of one file to another. used during -rw phase of pax
  *	just as in rd_wrfile() we use a special write function to write the
  *	destination file so we can properly copy files with holes.
+ * Return:
+ *	0 if ok, -1 if any error.
  */
 
-void
+int
 cp_file(ARCHD *arcn, int fd1, int fd2)
 {
 	int cnt;
@@ -783,6 +790,7 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
 	int rem;
 	int sz = MINFBSZ;
 	struct stat sb, origsb;
+	int rv = 0;
 
 	/*
 	 * check for holes in the source file. If none, we will use regular
@@ -797,8 +805,10 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
 	 * if Mflag is set, use the actual mtime instead.
 	 */
 	origsb = arcn->sb;
-	if (Mflag && (fstat(fd1, &origsb) < 0))
+	if (Mflag && (fstat(fd1, &origsb) < 0)) {
 		syswarn(1, errno, "Failed stat on %s", arcn->org_name);
+		rv = -1;
+	}
 
 	/*
 	 * pass the blocksize of the file being written to the write routine,
@@ -830,17 +840,22 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
 	/*
 	 * check to make sure the copy is valid.
 	 */
-	if (res < 0)
+	if (res < 0) {
 		syswarn(1, errno, "Failed write during copy of %s to %s",
 			arcn->org_name, arcn->name);
-	else if (cpcnt != arcn->sb.st_size)
+		rv = -1;
+	} else if (cpcnt != arcn->sb.st_size) {
 		tty_warn(1, "File %s changed size during copy to %s",
 			arcn->org_name, arcn->name);
-	else if (fstat(fd1, &sb) < 0)
+		rv = -1;
+	} else if (fstat(fd1, &sb) < 0) {
 		syswarn(1, errno, "Failed stat of %s", arcn->org_name);
-	else if (origsb.st_mtime != sb.st_mtime)
+		rv = -1;
+	} else if (origsb.st_mtime != sb.st_mtime) {
 		tty_warn(1, "File %s was modified during copy to %s",
 			arcn->org_name, arcn->name);
+		rv = -1;
+	}
 
 	/*
 	 * if the last block has a file hole (all zero), we must make sure this
@@ -848,9 +863,11 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
 	 * written. just closing with the file offset moved forward may not put
 	 * a hole at the end of the file.
 	 */
-	if (!no_hole && isem && (arcn->sb.st_size > 0L))
-		file_flush(fd2, fnm, isem);
-	return;
+	if (!no_hole && isem && (arcn->sb.st_size > 0L)) {
+		if (file_flush(fd2, fnm, isem) < 0)
+			rv = -1;
+	}
+	return rv;
 }
 
 /*

Index: src/bin/pax/extern.h
diff -u src/bin/pax/extern.h:1.59.36.1 src/bin/pax/extern.h:1.59.36.2
--- src/bin/pax/extern.h:1.59.36.1	Fri Apr 30 14:07:02 2021
+++ src/bin/pax/extern.h	Wed Aug  7 10:55:29 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: extern.h,v 1.59.36.1 2021/04/30 14:07:02 martin Exp $	*/
+/*	$NetBSD: extern.h,v 1.59.36.2 2024/08/07 10:55:29 martin Exp $	*/
 
 /*-
  * Copyright (c) 1992 Keith Muller.
@@ -115,7 +115,7 @@ int rd_wrbuf(char *, int);
 int wr_skip(off_t);
 int wr_rdfile(ARCHD *, int, off_t *);
 int rd_wrfile(ARCHD *, int, off_t *);
-void cp_file(ARCHD *, int, int);
+int cp_file(ARCHD *, int, int);
 int buf_fill(void);
 int buf_flush(int);
 
@@ -150,6 +150,7 @@ extern char *gnu_name_string, *gnu_link_
 extern size_t gnu_name_length, gnu_link_length;
 extern char *xtmp_name;
 int file_creat(ARCHD *, int);
+void file_cleanup(ARCHD *, int);
 void file_close(ARCHD *, int);
 int lnk_creat(ARCHD *, int *);
 int cross_lnk(ARCHD *);
@@ -162,7 +163,7 @@ int set_ids(char *, uid_t, gid_t);
 void set_pmode(char *, mode_t);
 void set_chflags(char *fnm, u_int32_t flags);
 int file_write(int, char *, int, int *, int *, int, char *);
-void file_flush(int, char *, int);
+int file_flush(int, char *, int);
 void rdfile_close(ARCHD *, int *);
 int set_crc(ARCHD *, int);
 

Index: src/bin/pax/file_subs.c
diff -u src/bin/pax/file_subs.c:1.64 src/bin/pax/file_subs.c:1.64.2.1
--- src/bin/pax/file_subs.c:1.64	Wed Mar 20 03:13:39 2019
+++ src/bin/pax/file_subs.c	Wed Aug  7 10:55:29 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: file_subs.c,v 1.64 2019/03/20 03:13:39 gutteridge Exp $	*/
+/*	$NetBSD: file_subs.c,v 1.64.2.1 2024/08/07 10:55:29 martin Exp $	*/
 
 /*-
  * Copyright (c) 1992 Keith Muller.
@@ -42,7 +42,7 @@
 #if 0
 static char sccsid[] = "@(#)file_subs.c	8.1 (Berkeley) 5/31/93";
 #else
-__RCSID("$NetBSD: file_subs.c,v 1.64 2019/03/20 03:13:39 gutteridge Exp $");
+__RCSID("$NetBSD: file_subs.c,v 1.64.2.1 2024/08/07 10:55:29 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -179,11 +179,43 @@ file_creat(ARCHD *arcn, int write_to_har
 }
 
 /*
+ * file_cleanup()
+ *	Close file descriptor to a file just created by pax.
+ *	Unlinks any temporary file.
+ */
+
+void
+file_cleanup(ARCHD *arcn, int fd)
+{
+	char *tmp_name;
+
+	if (fd < 0)
+		return;
+
+	tmp_name = (arcn->tmp_name != NULL) ? arcn->tmp_name : arcn->name;
+
+	if (close(fd) < 0)
+		syswarn(0, errno, "Cannot close file descriptor on %s",
+		    tmp_name);
+
+	/* No temporary file to cleanup? */
+	if (arcn->tmp_name == NULL)
+		return;
+
+	/* Cleanup the temporary file. */
+	if (unlink(arcn->tmp_name) < 0) {
+		syswarn(0, errno, "Cannot unlink %s", arcn->tmp_name);
+	}
+
+	free(arcn->tmp_name);
+	arcn->tmp_name = NULL;
+	xtmp_name = NULL;
+}
+
+/*
  * file_close()
  *	Close file descriptor to a file just created by pax. Sets modes,
  *	ownership and times as required.
- * Return:
- *	0 for success, -1 for failure
  */
 
 void
@@ -1039,9 +1071,11 @@ file_write(int fd, char *str, int cnt, i
  *	when the last file block in a file is zero, many file systems will not
  *	let us create a hole at the end. To get the last block with zeros, we
  *	write the last BYTE with a zero (back up one byte and write a zero).
+ * Return:
+ *	0 if was able to flush the file, -1 otherwise
  */
 
-void
+int
 file_flush(int fd, char *fname, int isempt)
 {
 	static char blnk[] = "\0";
@@ -1051,19 +1085,21 @@ file_flush(int fd, char *fname, int isem
 	 * filled with all zeros.
 	 */
 	if (!isempt)
-		return;
+		return 0;
 
 	/*
 	 * move back one byte and write a zero
 	 */
 	if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) {
 		syswarn(1, errno, "Failed seek on file %s", fname);
-		return;
+		return -1;
 	}
 
-	if (write_with_restart(fd, blnk, 1) < 0)
+	if (write_with_restart(fd, blnk, 1) < 0) {
 		syswarn(1, errno, "Failed write to file %s", fname);
-	return;
+		return -1;
+	}
+	return 0;
 }
 
 /*

Index: src/bin/pax/ftree.c
diff -u src/bin/pax/ftree.c:1.42 src/bin/pax/ftree.c:1.42.34.1
--- src/bin/pax/ftree.c:1.42	Thu Sep 27 00:44:59 2012
+++ src/bin/pax/ftree.c	Wed Aug  7 10:55:29 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: ftree.c,v 1.42 2012/09/27 00:44:59 christos Exp $	*/
+/*	$NetBSD: ftree.c,v 1.42.34.1 2024/08/07 10:55:29 martin Exp $	*/
 
 /*-
  * Copyright (c) 1992 Keith Muller.
@@ -71,7 +71,7 @@
 #if 0
 static char sccsid[] = "@(#)ftree.c	8.2 (Berkeley) 4/18/94";
 #else
-__RCSID("$NetBSD: ftree.c,v 1.42 2012/09/27 00:44:59 christos Exp $");
+__RCSID("$NetBSD: ftree.c,v 1.42.34.1 2024/08/07 10:55:29 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -194,7 +194,7 @@ ftree_start(void)
 	}
 
 	if (ftree_arg() < 0)
-		return -1;
+		return 0;
 	if (tflag && (atdir_start() < 0))
 		return -1;
 	return 0;

Index: src/bin/pax/pax.c
diff -u src/bin/pax/pax.c:1.49 src/bin/pax/pax.c:1.49.2.1
--- src/bin/pax/pax.c:1.49	Wed Apr 24 17:27:08 2019
+++ src/bin/pax/pax.c	Wed Aug  7 10:55:29 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: pax.c,v 1.49 2019/04/24 17:27:08 cheusov Exp $	*/
+/*	$NetBSD: pax.c,v 1.49.2.1 2024/08/07 10:55:29 martin Exp $	*/
 
 /*-
  * Copyright (c) 1992 Keith Muller.
@@ -44,7 +44,7 @@ __COPYRIGHT("@(#) Copyright (c) 1992, 19
 #if 0
 static char sccsid[] = "@(#)pax.c	8.2 (Berkeley) 4/18/94";
 #else
-__RCSID("$NetBSD: pax.c,v 1.49 2019/04/24 17:27:08 cheusov Exp $");
+__RCSID("$NetBSD: pax.c,v 1.49.2.1 2024/08/07 10:55:29 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -260,15 +260,24 @@ main(int argc, char **argv)
 		return exit_val;
 
 	/*
-	 * Keep a reference to cwd, so we can always come back home.
+	 * For any actions other than LIST, keep a reference to cwd, so
+	 * we can always come back home.
+	 *
+	 * For EXTRACT (pax -r) without --insecure, also save the path
+	 * to cwd to check for escape attempts.
 	 */
-	cwdfd = open(".", O_RDONLY);
-	if (cwdfd < 0) {
-		syswarn(1, errno, "Can't open current working directory.");
-		return exit_val;
+	if (act != LIST) {
+		cwdfd = open(".", O_RDONLY);
+		if (cwdfd < 0) {
+			syswarn(1, errno,
+			    "Can't open current working directory.");
+			return exit_val;
+		}
+		if (act == EXTRACT && secure) {
+			if (updatepath() == -1)
+				return exit_val;
+		}
 	}
-	if (updatepath() == -1)
-		return exit_val;
 
 	/*
 	 * Where should we put temporary files?

Index: src/tests/bin/pax/t_pax.sh
diff -u src/tests/bin/pax/t_pax.sh:1.1 src/tests/bin/pax/t_pax.sh:1.1.36.1
--- src/tests/bin/pax/t_pax.sh:1.1	Sat Mar 17 16:33:11 2012
+++ src/tests/bin/pax/t_pax.sh	Wed Aug  7 10:55:29 2024
@@ -1,4 +1,4 @@
-# $NetBSD: t_pax.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $
+# $NetBSD: t_pax.sh,v 1.1.36.1 2024/08/07 10:55:29 martin Exp $
 #
 # Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -48,7 +48,101 @@ append_body() {
 	atf_check -s eq:0 -o empty -e empty cmp file1.tar file2.tar
 }
 
+atf_test_case pr41736
+pr41736_head()
+{
+	atf_set "descr" "Test pax exits with 0 if stdin file list is empty"
+}
+pr41736_body()
+{
+	atf_check pax -rw . </dev/null
+}
+
+atf_test_case pr44498
+pr44498_head()
+{
+	atf_set "descr" "Ensure pax list operation works without getcwd"
+	atf_set "require.user" "unprivileged"
+}
+pr44498_body()
+{
+	mkdir foo foo/bar baz
+	chmod 111 foo
+	touch baz/quux
+	atf_check pax -w -x ustar -f baz.tar baz
+	atf_check -o 'inline:baz\nbaz/quux\n' \
+	    sh -c '{ cd foo/bar && exec pax; } <baz.tar'
+}
+
+atf_test_case pr44498_copy
+pr44498_copy_head()
+{
+	atf_set "descr" \
+	    "Ensure pax insecure copy operation works without getcwd"
+	atf_set "require.user" "unprivileged"
+}
+pr44498_copy_body()
+{
+	mkdir foo foo/bar foo/bar/baz
+	chmod 111 foo
+	touch foo/bar/quux
+	atf_check sh -c '{ cd foo/bar && exec pax -rw quux baz/.; }'
+}
+
+atf_test_case pr44498_insecureextract
+pr44498_insecureextract_head()
+{
+	atf_set "descr" \
+	    "Ensure pax insecure extract operation works without getcwd"
+	atf_set "require.user" "unprivileged"
+}
+pr44498_insecureextract_body()
+{
+	mkdir foo foo/bar baz
+	chmod 111 foo
+	touch baz/quux
+	atf_check pax -w -x ustar -f baz.tar baz
+	atf_check sh -c '{ cd foo/bar && exec pax -r --insecure; } <baz.tar'
+}
+
+atf_test_case pr44498_listwd
+pr44498_listwd_head()
+{
+	atf_set "descr" "Ensure pax list operation works without working dir"
+	atf_set "require.user" "unprivileged"
+}
+pr44498_listwd_body()
+{
+	mkdir foo baz
+	chmod 111 foo
+	touch baz/quux
+	atf_check pax -w -x ustar -f baz.tar baz
+	atf_check -o 'inline:baz\nbaz/quux\n' \
+	    sh -c '{ cd foo && exec pax; } <baz.tar'
+}
+
+atf_test_case pr44498_write
+pr44498_write_head()
+{
+	atf_set "descr" "Ensure pax write operation works without getcwd"
+	atf_set "require.user" "unprivileged"
+}
+pr44498_write_body()
+{
+	mkdir foo foo/bar
+	touch foo/bar/quux
+	chmod 111 foo
+	atf_check sh -c '{ cd foo/bar && pax -w -x ustar .; } >bar.tar'
+	atf_check -o 'inline:.\n./quux\n' pax -f bar.tar
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case append
+	atf_add_test_case pr41736
+	atf_add_test_case pr44498
+	atf_add_test_case pr44498_copy
+	atf_add_test_case pr44498_insecureextract
+	atf_add_test_case pr44498_listwd
+	atf_add_test_case pr44498_write
 }

Reply via email to