"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Omar Polo <op@omarpolo.com>
Subject:
[WIP] landlock for got-portable
To:
gameoftrees@openbsd.org
Date:
Thu, 23 Sep 2021 08:42:21 +0200

Download raw body.

Thread
Hello,

Recently I've been playing with landlock a bit, and when I've seen the
announcement of the first portable version, I thought of giving a try to
implement something for got too.

Landlock is a fairly new linux addition, available from 5.13 onwards,
that is similar to unveil.  The goal of landlock is to reduce the
portion of the file system that are accessible to a process.

The official documentation is available here, but a short description
follows.

	https://landlock.io/
	https://landlock.io/linux-doc/landlock-v34/userspace-api/landlock.html


How landlock works
==================

Unlike seccomp, which works on a syscall-number basis, landlock works on
top of "abstract operations", like "execute a file", "remove a
directory", "create a fifo" etc

The first step is to obtain a file descriptor with
landlock_create_ruleset().  This function takes a struct with a bitmask
of all the actions that following policies may enable.  Actions not
listed here are implicitly denied.

Then it's possible to add rules to the policy using landlock_add_rule()
specifying per-path permissions, just like with unveil(2).

To enforce the ruleset it's needed to call prctl(PR_SET_NO_NEW_PRIVS) to
restrict the process from gaining more privileges and apply the ruleset
with landlock_restrict_self.


Major differences with unveil(2)
================================

There are two major differences between the landlock and unveil
semantics:


  1.  landlock persist across execve(2)

      This is a major pain because, even if stsp@ told me that unveil
      usage in got already takes this assumption into account, ld.so (or
      whom it may concern) in the executed binary can't open the shared
      libraries.  I'm addressing this with a allowing "rx" on "/lib64",
      but this works only on the linux machine I'm using, I don't know
      if it's possible *at runtime* too obtain the path of the linked
      libraries and add that, or do some other kinds of "magic" in this
      regard.

      This is a (the only?) major problem that I still have to sort out.


  2.  landlock is "stackable"

      If I'm reading the documentation correctly, even when a landlock
      policy is enabled, the process can still add other (strictier)
      policies.  In this regard, landlock behaves like pledge(2).  This
      means that executed programs can still use landlock, which is
      kinda nice.


  3.  landlock can't really allow to execute a file

      The landlock equivalent of

		unveil("/bin/ls", "x");
		unveil(NULL, NULL);
		execlp("/bin/ls", "ls", NULL);

      don't work.  It took me a while to notice, in particular coming
      from OpenBSD, but the documentation effectively talks about
      path_beneath.

      Instead, one has to allow "rx" on the directory containing the
      binaries.


How I'm trying to adding it in -portable
========================================

Only inside the got/ directory there are 95 matches for "unveil" and
even if some of them are the "unveil" pledge promise I can't
realistically change every call to unveil into, say,
got_landlock_something_something.

At the moment, unveil in -portable is handled by a macro in got_compat.h

	#define unveil(s, p) 0

My idea is to provide an unveil-like API in compat/unveil.c based on
landlock.  Doing so allowed to add landlock support by breaking least
code possible.

The result is pretty good IMHO, and it's possible to reuse unveil.c on
other projects too.


Results from the regression suite
=================================

I'm attaching two files with the results from the regression test with
and without landlock.  Unfortunately, various tests in ./clone.sh now
fails.  I still haven't really looked into them too much.

The tests were run on a fedora 33 installation, with libmd manually
installed.

$ uname -srvp
Linux 5.13.16-100.fc33.x86_64 #1 SMP Mon Sep 13 12:35:26 UTC 2021 x86_64


Conclusion
==========

There are some points that needs to be addressed, and for some of these
I need help :)

  1.  at the top of unveil.c the #ifdef should read
      HAVE_LINUX_LANDLOCK_H and not __linux__.  I've added the check for
      the linux/landlock.h header in configure.ac, but that macro
      doesn't seem to be defined when compiling unveil.c.


  2.  got has various calls to

	unveil("/path/to/executable", "x");

      which doesn't really translate well to landlock.  It's better to
      change these calls in lib/privsep.c, or "magically" fix things in
      unveil.c?  (after the open(path, O_PATH) call I can still do a
      fstat to check if it's a file or not.)


  3.  I don't have any idea on how to "unveil" the directories with the
      libraries.  Probably we can take advantage that `got' and all the
      helpers are linked to the same set of libraries in some way?


I don't really know which other programs uses landlock in the wild.  I
know GNU tar was modified by the landlock dev to use it:

(friendly remainder that by opening the link below your eyes will be
attacked by the GNU C coding style :P)

	https://lists.gnu.org/archive/html/bug-tar/2021-04/msg00002.html

and I've added it in a personal project (a Gemini server).  Probably
other projects uses it too, but I haven't been able to find them.

Compared to seccomp, landlock is almost nice to use, and I think that
got on linux could benefit from it.  I don't think it'll be this
straightforward to add seccomp support.

Well, at least I had some fun.  Thanks for reading this far!

Cheers

Omar Polo

P.S.: having `tog' working is cool :D


From 662ece327863d9c8514450e4a0ee9277065a1b00 Mon Sep 17 00:00:00 2001
From: Omar Polo <op@omarpolo.com>
Date: Thu, 23 Sep 2021 08:50:19 +0200
Subject: [PATCH] add a landlock-based implementation of unveil for linux

---
 compat/Makefile.am   |   1 +
 compat/unveil.c      | 188 +++++++++++++++++++++++++++++++++++++++++++
 configure.ac         |   2 +
 include/got_compat.h |   5 +-
 lib/privsep.c        |  15 +---
 5 files changed, 198 insertions(+), 13 deletions(-)
 create mode 100644 compat/unveil.c

diff --git a/compat/Makefile.am b/compat/Makefile.am
index 29fcf763..e26eef57 100644
--- a/compat/Makefile.am
+++ b/compat/Makefile.am
@@ -30,6 +30,7 @@ libopenbsd_compat_a_SOURCES =  \
 	strsep.c \
 	strtonum.c \
 	uuid.c \
+	unveil.c \
 	imsg.h \
 	queue.h \
 	tree.h
diff --git a/compat/unveil.c b/compat/unveil.c
new file mode 100644
index 00000000..812e170a
--- /dev/null
+++ b/compat/unveil.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * FIXME: should really be
+ *
+ * #ifdef HAVE_LINUX_LANDLOCK_H
+ *
+ */
+#ifdef __linux__
+
+#include <linux/landlock.h>
+#include <linux/prctl.h>
+
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This is not really OpenBSD' unveil() but rather a "unveil
+ * implementation" on top of Linux' landlock.
+ *
+ *                           -*-*-
+ *
+ * What's the deal with landlock?  While distro with linux >= 5.13
+ * have the struct declarations, libc wrappers are missing.  The
+ * sample landlock code provided by the authors includes these "shims"
+ * in their example for the landlock API until libc provides them.
+ */
+
+#ifndef landlock_create_ruleset
+static inline int
+landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size,
+    __u32 flags)
+{
+	return syscall(__NR_landlock_create_ruleset, attr, size, flags);
+}
+#endif
+
+#ifndef landlock_add_rule
+static inline int
+landlock_add_rule(int ruleset_fd, enum landlock_rule_type type,
+    const void *attr, __u32 flags)
+{
+	return syscall(__NR_landlock_add_rule, ruleset_fd, type, attr, flags);
+}
+#endif
+
+#ifndef landlock_restrict_self
+static inline int
+landlock_restrict_self(int ruleset_fd, __u32 flags)
+{
+	return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
+}
+#endif
+
+static int landlock_fd = -1;
+
+static int
+open_landlock(void)
+{
+	struct landlock_ruleset_attr rattr = {
+		.handled_access_fs =	LANDLOCK_ACCESS_FS_EXECUTE	|
+					LANDLOCK_ACCESS_FS_WRITE_FILE	|
+					LANDLOCK_ACCESS_FS_READ_FILE	|
+					LANDLOCK_ACCESS_FS_READ_DIR	|
+					LANDLOCK_ACCESS_FS_REMOVE_DIR	|
+					LANDLOCK_ACCESS_FS_REMOVE_FILE	|
+					LANDLOCK_ACCESS_FS_MAKE_CHAR	|
+					LANDLOCK_ACCESS_FS_MAKE_DIR	|
+					LANDLOCK_ACCESS_FS_MAKE_REG	|
+					LANDLOCK_ACCESS_FS_MAKE_SOCK	|
+					LANDLOCK_ACCESS_FS_MAKE_FIFO	|
+					LANDLOCK_ACCESS_FS_MAKE_BLOCK	|
+					LANDLOCK_ACCESS_FS_MAKE_SYM,
+	};
+
+	return landlock_create_ruleset(&rattr, sizeof(rattr), 0);
+}
+
+static int
+parse_permissions(const char *permission)
+{
+	int perm = 0;
+
+	for (; *permission; ++permission) {
+		switch (*permission) {
+		case 'r':
+			perm |= LANDLOCK_ACCESS_FS_READ_FILE;
+			perm |= LANDLOCK_ACCESS_FS_READ_DIR;
+			break;
+		case 'w':
+			perm |= LANDLOCK_ACCESS_FS_WRITE_FILE;
+			break;
+		case 'x':
+			perm |= LANDLOCK_ACCESS_FS_EXECUTE;
+			break;
+		case 'c':
+			perm |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
+			perm |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
+			perm |= LANDLOCK_ACCESS_FS_MAKE_CHAR;
+			perm |= LANDLOCK_ACCESS_FS_MAKE_DIR;
+			perm |= LANDLOCK_ACCESS_FS_MAKE_REG;
+			perm |= LANDLOCK_ACCESS_FS_MAKE_SOCK;
+			perm |= LANDLOCK_ACCESS_FS_MAKE_FIFO;
+			perm |= LANDLOCK_ACCESS_FS_MAKE_BLOCK;
+			perm |= LANDLOCK_ACCESS_FS_MAKE_SYM;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	return perm;
+}
+
+int
+unveil(const char *path, const char *permissions)
+{
+	struct landlock_path_beneath_attr pb;
+	int fd, err;
+
+	if (landlock_fd == -1)
+		landlock_fd = open_landlock();
+
+	if (path == NULL && permissions == NULL) {
+		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
+			return -1;
+
+		if (landlock_restrict_self(landlock_fd, 0))
+			return -1;
+
+		close(landlock_fd);
+		landlock_fd = -1;
+		return 0;
+	}
+
+	if (path == NULL ||
+	    permissions == NULL ||
+	    (pb.allowed_access = parse_permissions(permissions)) == -1) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((pb.parent_fd = open(path, O_PATH)) == -1)
+		return -1;
+
+	err = landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH,
+	    &pb, 0);
+	if (err)
+                goto err;
+
+	close(pb.parent_fd);
+	return 0;
+
+err:
+	close(pb.parent_fd);
+	return -1;
+}
+
+#else
+
+int
+unveil(const char *path, const char *permissions)
+{
+	return 0;
+}
+
+#endif
diff --git a/configure.ac b/configure.ac
index cdd6e159..68d8ed68 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_CHECK_HEADERS([ \
 	fcntl.h \
 	langinfo.h \
 	limits.h \
+	linux/landlock.h \
 	locale.h \
 	netdb.h \
 	netinet/in.h \
@@ -135,6 +136,7 @@ AC_REPLACE_FUNCS([ \
 	strndup \
 	strsep \
 	strtonum \
+	unveil \
 ])
 
 # Always use our getopt because 1) glibc's doesn't enforce argument order 2)
diff --git a/include/got_compat.h b/include/got_compat.h
index 6270fd4e..062d88e7 100644
--- a/include/got_compat.h
+++ b/include/got_compat.h
@@ -35,7 +35,6 @@
 
 #ifndef __OpenBSD__
 #define pledge(s, p) (0)
-#define unveil(s, p) (0)
 #endif
 
 #ifndef INFTIM
@@ -211,3 +210,7 @@ int	BSDgetopt(int, char *const *, const char *);
 /* mergesort.c */
 int mergesort(void *, size_t, size_t, int (*)(const void *, const void *));
 #endif
+
+#ifndef HAVE_UNVEIL
+int	unveil(const char *, const char *);
+#endif
diff --git a/lib/privsep.c b/lib/privsep.c
index 05b4a3a9..1d70046f 100644
--- a/lib/privsep.c
+++ b/lib/privsep.c
@@ -2732,22 +2732,13 @@ const struct got_error *
 got_privsep_unveil_exec_helpers(void)
 {
 	const char *helpers[] = {
-	    GOT_PATH_PROG_READ_PACK,
-	    GOT_PATH_PROG_READ_OBJECT,
-	    GOT_PATH_PROG_READ_COMMIT,
-	    GOT_PATH_PROG_READ_TREE,
-	    GOT_PATH_PROG_READ_BLOB,
-	    GOT_PATH_PROG_READ_TAG,
-	    GOT_PATH_PROG_READ_GITCONFIG,
-	    GOT_PATH_PROG_READ_GOTCONFIG,
-	    GOT_PATH_PROG_FETCH_PACK,
-	    GOT_PATH_PROG_INDEX_PACK,
-	    GOT_PATH_PROG_SEND_PACK,
+		"/lib64",
+		GOT_STRINGVAL(GOT_LIBEXECDIR),
 	};
 	size_t i;
 
 	for (i = 0; i < nitems(helpers); i++) {
-		if (unveil(helpers[i], "x") == 0)
+		if (unveil(helpers[i], "rx") == 0)
 			continue;
 		return got_error_from_errno2("unveil", helpers[i]);
 	}
-- 
2.31.1

make -C regress/cmdline -f Makefile.linux
make[1]: Entering directory '/home/op/got-portable/regress/cmdline'
./checkout.sh -q -r "/tmp"
./update.sh -q -r "/tmp"
--- /tmp/got-test-update_symlink_conflicts-8x0OXZ7D/stdout.expected	2021-09-23 09:00:58.532444826 +0200
+++ /tmp/got-test-update_symlink_conflicts-8x0OXZ7D/stdout	2021-09-23 09:00:58.529444810 +0200
@@ -1,10 +1,3 @@
 C  alpha.link
 C  dotgotbar.link
 C  dotgotfoo.link
-C  epsilon/beta.link
-C  epsilon.link
-C  new.link
-C  nonexistent.link
-G  zeta.link
-Updated to refs/heads/master: 0a032a801cf95e20eb5c1d5339b8abfc70e58a36
-Files with new merge conflicts: 7
test failed; leaving test data in /tmp/got-test-update_symlink_conflicts-8x0OXZ7D
./status.sh -q -r "/tmp"
On branch master
nothing to commit, working tree clean
--- /tmp/got-test-status_shows_no_mods_after_complete_merge-QFzyX0Oz/stdout.expected	2021-09-23 09:01:00.794457417 +0200
+++ /tmp/got-test-status_shows_no_mods_after_complete_merge-QFzyX0Oz/stdout	2021-09-23 09:01:00.845457701 +0200
@@ -1,2 +1 @@
-G  numbers
-Updated to refs/heads/master: d91a719b0ad5474f1166dc44a72f275426956bc7
+Already up-to-date
test failed; leaving test data in /tmp/got-test-status_shows_no_mods_after_complete_merge-QFzyX0Oz
./log.sh -q -r "/tmp"
./add.sh -q -r "/tmp"
./rm.sh -q -r "/tmp"
./diff.sh -q -r "/tmp"
./blame.sh -q -r "/tmp"
--- /tmp/got-test-blame_all_lines_replaced-oSGxx18H/stdout.expected	2021-09-23 09:01:06.920491514 +0200
+++ /tmp/got-test-blame_all_lines_replaced-oSGxx18H/stdout	2021-09-23 09:01:06.900491402 +0200
@@ -1,8 +0,0 @@
-1) 565dce7b 2021-09-23 flan_hac 1
-2) 565dce7b 2021-09-23 flan_hac 2
-3) 565dce7b 2021-09-23 flan_hac 3
-4) 565dce7b 2021-09-23 flan_hac 4
-5) 565dce7b 2021-09-23 flan_hac 5
-6) 565dce7b 2021-09-23 flan_hac 6
-7) 565dce7b 2021-09-23 flan_hac 7
-8) 565dce7b 2021-09-23 flan_hac 8
test failed; leaving test data in /tmp/got-test-blame_all_lines_replaced-oSGxx18H
--- /tmp/got-test-blame_lines_shifted_up-mNJZMH09/stdout.expected	2021-09-23 09:01:07.051492243 +0200
+++ /tmp/got-test-blame_lines_shifted_up-mNJZMH09/stdout	2021-09-23 09:01:07.050492237 +0200
@@ -1,8 +1,3 @@
-1) 565dce7b 2021-09-23 flan_hac 1
-2) 565dce7b 2021-09-23 flan_hac 2
-3) 257b81fb 2021-09-23 flan_hac foo
-4) 257b81fb 2021-09-23 flan_hac bar
-5) 257b81fb 2021-09-23 flan_hac baz
-6) 565dce7b 2021-09-23 flan_hac 6
-7) 565dce7b 2021-09-23 flan_hac 7
-8) 565dce7b 2021-09-23 flan_hac 8
+1) 257b81fb 2021-09-23 flan_hac foo
+2) 257b81fb 2021-09-23 flan_hac bar
+3) 257b81fb 2021-09-23 flan_hac baz
test failed; leaving test data in /tmp/got-test-blame_lines_shifted_up-mNJZMH09
--- /tmp/got-test-blame_lines_shifted_down-3YF50Oyl/stdout.expected	2021-09-23 09:01:07.184492983 +0200
+++ /tmp/got-test-blame_lines_shifted_down-3YF50Oyl/stdout	2021-09-23 09:01:07.182492972 +0200
@@ -1,11 +1,3 @@
-01) 892ec2ab 2021-09-23 flan_hac 1
-02) 892ec2ab 2021-09-23 flan_hac 2
-03) c8d2986a 2021-09-23 flan_hac foo
-04) c8d2986a 2021-09-23 flan_hac bar
-05) c8d2986a 2021-09-23 flan_hac baz
-06) 892ec2ab 2021-09-23 flan_hac 3
-07) 892ec2ab 2021-09-23 flan_hac 4
-08) 892ec2ab 2021-09-23 flan_hac 5
-09) 892ec2ab 2021-09-23 flan_hac 6
-10) 892ec2ab 2021-09-23 flan_hac 7
-11) c8d2986a 2021-09-23 flan_hac 8
+1) c8d2986a 2021-09-23 flan_hac foo
+2) c8d2986a 2021-09-23 flan_hac bar
+3) c8d2986a 2021-09-23 flan_hac baz
test failed; leaving test data in /tmp/got-test-blame_lines_shifted_down-3YF50Oyl
./branch.sh -q -r "/tmp"
./tag.sh -q -r "/tmp"
./ref.sh -q -r "/tmp"
./commit.sh -q -r "/tmp"
./revert.sh -q -r "/tmp"
--- /tmp/got-test-revert_patch-X6ShK4fw/stdout.expected	2021-09-23 09:01:13.938530575 +0200
+++ /tmp/got-test-revert_patch-X6ShK4fw/stdout	2021-09-23 09:01:13.925530503 +0200
@@ -1,34 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-revert this change? [y/n/q] n
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-revert this change? [y/n/q] n
------------------------------------------------
-@@ -13,4 +13,4 @@
- 13
- 14
- 15
--16
-+c
------------------------------------------------
-M  numbers (change 3 of 3)
-revert this change? [y/n/q] n
test failed; leaving test data in /tmp/got-test-revert_patch-X6ShK4fw
--- /tmp/got-test-revert_patch_one_change-tg8plxbC/stdout.expected	2021-09-23 09:01:14.202532044 +0200
+++ /tmp/got-test-revert_patch_one_change-tg8plxbC/stdout	2021-09-23 09:01:14.190531978 +0200
@@ -1,11 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+aa
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 1)
-revert this change? [y/n/q] y
test failed; leaving test data in /tmp/got-test-revert_patch_one_change-tg8plxbC
./cherrypick.sh -q -r "/tmp"
--- /tmp/got-test-cherrypick_symlink_conflicts-Jv2WWaeI/stdout.expected	2021-09-23 09:01:16.056542364 +0200
+++ /tmp/got-test-cherrypick_symlink_conflicts-Jv2WWaeI/stdout	2021-09-23 09:01:16.055542358 +0200
@@ -1,11 +1 @@
 C  alpha.link
-C  epsilon/beta.link
-?  boo.link
-C  epsilon.link
-C  dotgotbar.link
-C  dotgotfoo.link
-D  nonexistent.link
-!  zeta.link
-C  new.link
-Merged commit c7e7b2e6d44dc944f734b2bbd402020d2d5cd20a
-Files with new merge conflicts: 6
test failed; leaving test data in /tmp/got-test-cherrypick_symlink_conflicts-Jv2WWaeI
./backout.sh -q -r "/tmp"
./rebase.sh -q -r "/tmp"
./import.sh -q -r "/tmp"
--- /tmp/got-test-import_basic-Eq08Yp0g/stdout.expected	2021-09-23 09:01:19.178559740 +0200
+++ /tmp/got-test-import_basic-Eq08Yp0g/stdout	2021-09-23 09:01:19.174559718 +0200
@@ -1,5 +1,5 @@
-A  /tmp/got-test-import_basic-Eq08Yp0g/tree/gamma/delta
 A  /tmp/got-test-import_basic-Eq08Yp0g/tree/epsilon/zeta
+A  /tmp/got-test-import_basic-Eq08Yp0g/tree/gamma/delta
 A  /tmp/got-test-import_basic-Eq08Yp0g/tree/alpha
 A  /tmp/got-test-import_basic-Eq08Yp0g/tree/beta
 Created branch refs/heads/main with commit d9f8f21af71a72ca781a3edce5e178ef39984882
test failed; leaving test data in /tmp/got-test-import_basic-Eq08Yp0g
./histedit.sh -q -r "/tmp"
./integrate.sh -q -r "/tmp"
./stage.sh -q -r "/tmp"
--- /tmp/got-test-stage_patch-QVQba0G2/stdout.expected	2021-09-23 09:01:25.193593219 +0200
+++ /tmp/got-test-stage_patch-QVQba0G2/stdout	2021-09-23 09:01:25.180593147 +0200
@@ -1,34 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-stage this change? [y/n/q] n
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-stage this change? [y/n/q] n
------------------------------------------------
-@@ -13,4 +13,4 @@
- 13
- 14
- 15
--16
-+c
------------------------------------------------
-M  numbers (change 3 of 3)
-stage this change? [y/n/q] n
test failed; leaving test data in /tmp/got-test-stage_patch-QVQba0G2
--- /tmp/got-test-stage_patch_twice-XdKna4Bl/stdout.expected	2021-09-23 09:01:25.270593648 +0200
+++ /tmp/got-test-stage_patch_twice-XdKna4Bl/stdout	2021-09-23 09:01:25.256593570 +0200
@@ -1,34 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-stage this change? [y/n/q] n
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-stage this change? [y/n/q] y
------------------------------------------------
-@@ -13,4 +13,4 @@
- 13
- 14
- 15
--16
-+c
------------------------------------------------
-M  numbers (change 3 of 3)
-stage this change? [y/n/q] n
test failed; leaving test data in /tmp/got-test-stage_patch_twice-XdKna4Bl
--- /tmp/got-test-stage_patch_quit-7tesL6Qk/stdout.expected	2021-09-23 09:01:25.749596314 +0200
+++ /tmp/got-test-stage_patch_quit-7tesL6Qk/stdout	2021-09-23 09:01:25.747596303 +0200
@@ -1,26 +1,2 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-stage this change? [y/n/q] y
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-stage this change? [y/n/q] q
 D  zzz
-stage this deletion? [y/n] n
+stage this deletion? [y/n] y
test failed; leaving test data in /tmp/got-test-stage_patch_quit-7tesL6Qk
--- /tmp/got-test-stage_incomplete_script-Pprvtteo/stderr.expected	2021-09-23 09:01:25.824596731 +0200
+++ /tmp/got-test-stage_incomplete_script-Pprvtteo/stderr	2021-09-23 09:01:25.823596726 +0200
@@ -1 +1 @@
-got: invalid patch choice
+got: no changes to stage
test failed; leaving test data in /tmp/got-test-stage_incomplete_script-Pprvtteo
./unstage.sh -q -r "/tmp"
test failed; leaving test data in /tmp/got-test-unstage_patch-mgkCln68
--- /tmp/got-test-unstage_patch_quit-bfrycmyJ/stdout.expected	2021-09-23 09:01:27.364605303 +0200
+++ /tmp/got-test-unstage_patch_quit-bfrycmyJ/stdout	2021-09-23 09:01:27.362605292 +0200
@@ -1,27 +1,3 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-unstage this change? [y/n/q] y
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-unstage this change? [y/n/q] q
-G  numbers
 D  zzz
-unstage this deletion? [y/n] n
+unstage this deletion? [y/n] y
+D  zzz
test failed; leaving test data in /tmp/got-test-unstage_patch_quit-bfrycmyJ
./cat.sh -q -r "/tmp"
./clone.sh -q -r "/tmp"
test failed; leaving test data in /tmp/got-test-clone_basic-ALUcGjak
test failed; leaving test data in /tmp/got-test-clone_list-cD8LDeq9
test failed; leaving test data in /tmp/got-test-clone_branch-9Ugp0WIY
test failed; leaving test data in /tmp/got-test-clone_all-NBwBZOgC
test failed; leaving test data in /tmp/got-test-clone_mirror-kt7Uu7uL
test failed; leaving test data in /tmp/got-test-clone_mirror_all-9TKUv5gf
test failed; leaving test data in /tmp/got-test-clone_reference-m4qns2XZ
test failed; leaving test data in /tmp/got-test-clone_reference-CLKVFBdo
test failed; leaving test data in /tmp/got-test-clone_reference_mirror-zlwun1kP
test failed; leaving test data in /tmp/got-test-clone_multiple_branches-2fnJ4Gya
make[1]: Leaving directory '/home/op/got-portable/regress/cmdline'
make -C regress/cmdline -f Makefile.linux
make[1]: Entering directory '/home/op/got-portable/regress/cmdline'
./checkout.sh -q -r "/tmp"
./update.sh -q -r "/tmp"
./status.sh -q -r "/tmp"
On branch master
nothing to commit, working tree clean
--- /tmp/got-test-status_shows_no_mods_after_complete_merge-9eSe4088/stdout.expected	2021-09-23 08:55:52.438663670 +0200
+++ /tmp/got-test-status_shows_no_mods_after_complete_merge-9eSe4088/stdout	2021-09-23 08:55:52.480663921 +0200
@@ -1,2 +1 @@
-G  numbers
-Updated to refs/heads/master: 09dcc80e1b1fb9014f13b46f8807e50db795b7db
+Already up-to-date
test failed; leaving test data in /tmp/got-test-status_shows_no_mods_after_complete_merge-9eSe4088
./log.sh -q -r "/tmp"
./add.sh -q -r "/tmp"
./rm.sh -q -r "/tmp"
./diff.sh -q -r "/tmp"
./blame.sh -q -r "/tmp"
--- /tmp/got-test-blame_all_lines_replaced-Lrr28OSf/stdout.expected	2021-09-23 08:55:58.463699614 +0200
+++ /tmp/got-test-blame_all_lines_replaced-Lrr28OSf/stdout	2021-09-23 08:55:58.443699494 +0200
@@ -1,8 +0,0 @@
-1) b811a7be 2021-09-23 flan_hac 1
-2) b811a7be 2021-09-23 flan_hac 2
-3) b811a7be 2021-09-23 flan_hac 3
-4) b811a7be 2021-09-23 flan_hac 4
-5) b811a7be 2021-09-23 flan_hac 5
-6) b811a7be 2021-09-23 flan_hac 6
-7) b811a7be 2021-09-23 flan_hac 7
-8) b811a7be 2021-09-23 flan_hac 8
test failed; leaving test data in /tmp/got-test-blame_all_lines_replaced-Lrr28OSf
--- /tmp/got-test-blame_lines_shifted_up-dneRvl2u/stdout.expected	2021-09-23 08:55:58.591700377 +0200
+++ /tmp/got-test-blame_lines_shifted_up-dneRvl2u/stdout	2021-09-23 08:55:58.589700365 +0200
@@ -1,8 +1,3 @@
-1) b811a7be 2021-09-23 flan_hac 1
-2) b811a7be 2021-09-23 flan_hac 2
-3) b76a630b 2021-09-23 flan_hac foo
-4) b76a630b 2021-09-23 flan_hac bar
-5) b76a630b 2021-09-23 flan_hac baz
-6) b811a7be 2021-09-23 flan_hac 6
-7) b811a7be 2021-09-23 flan_hac 7
-8) b811a7be 2021-09-23 flan_hac 8
+1) b76a630b 2021-09-23 flan_hac foo
+2) b76a630b 2021-09-23 flan_hac bar
+3) b76a630b 2021-09-23 flan_hac baz
test failed; leaving test data in /tmp/got-test-blame_lines_shifted_up-dneRvl2u
--- /tmp/got-test-blame_lines_shifted_down-EaqmRisS/stdout.expected	2021-09-23 08:55:58.723701165 +0200
+++ /tmp/got-test-blame_lines_shifted_down-EaqmRisS/stdout	2021-09-23 08:55:58.722701159 +0200
@@ -1,11 +1,3 @@
-01) b811a7be 2021-09-23 flan_hac 1
-02) b811a7be 2021-09-23 flan_hac 2
-03) b76a630b 2021-09-23 flan_hac foo
-04) b76a630b 2021-09-23 flan_hac bar
-05) b76a630b 2021-09-23 flan_hac baz
-06) b811a7be 2021-09-23 flan_hac 3
-07) b811a7be 2021-09-23 flan_hac 4
-08) b811a7be 2021-09-23 flan_hac 5
-09) b811a7be 2021-09-23 flan_hac 6
-10) b811a7be 2021-09-23 flan_hac 7
-11) b76a630b 2021-09-23 flan_hac 8
+1) b76a630b 2021-09-23 flan_hac foo
+2) b76a630b 2021-09-23 flan_hac bar
+3) b76a630b 2021-09-23 flan_hac baz
test failed; leaving test data in /tmp/got-test-blame_lines_shifted_down-EaqmRisS
./branch.sh -q -r "/tmp"
./tag.sh -q -r "/tmp"
./ref.sh -q -r "/tmp"
./commit.sh -q -r "/tmp"
./revert.sh -q -r "/tmp"
--- /tmp/got-test-revert_patch-Ju81pLFc/stdout.expected	2021-09-23 08:56:05.076739065 +0200
+++ /tmp/got-test-revert_patch-Ju81pLFc/stdout	2021-09-23 08:56:05.064738994 +0200
@@ -1,34 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-revert this change? [y/n/q] n
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-revert this change? [y/n/q] n
------------------------------------------------
-@@ -13,4 +13,4 @@
- 13
- 14
- 15
--16
-+c
------------------------------------------------
-M  numbers (change 3 of 3)
-revert this change? [y/n/q] n
test failed; leaving test data in /tmp/got-test-revert_patch-Ju81pLFc
--- /tmp/got-test-revert_patch_one_change-GWK3o66G/stdout.expected	2021-09-23 08:56:05.418741106 +0200
+++ /tmp/got-test-revert_patch_one_change-GWK3o66G/stdout	2021-09-23 08:56:05.405741028 +0200
@@ -1,11 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+aa
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 1)
-revert this change? [y/n/q] y
test failed; leaving test data in /tmp/got-test-revert_patch_one_change-GWK3o66G
./cherrypick.sh -q -r "/tmp"
./backout.sh -q -r "/tmp"
./rebase.sh -q -r "/tmp"
./import.sh -q -r "/tmp"
--- /tmp/got-test-import_basic-gKLS21HF/stdout.expected	2021-09-23 08:56:10.131769222 +0200
+++ /tmp/got-test-import_basic-gKLS21HF/stdout	2021-09-23 08:56:10.128769205 +0200
@@ -1,5 +1,5 @@
-A  /tmp/got-test-import_basic-gKLS21HF/tree/gamma/delta
 A  /tmp/got-test-import_basic-gKLS21HF/tree/epsilon/zeta
+A  /tmp/got-test-import_basic-gKLS21HF/tree/gamma/delta
 A  /tmp/got-test-import_basic-gKLS21HF/tree/alpha
 A  /tmp/got-test-import_basic-gKLS21HF/tree/beta
 Created branch refs/heads/main with commit ce2a4cd5c379ed47ce030ec34681c1ed2e349b0a
test failed; leaving test data in /tmp/got-test-import_basic-gKLS21HF
./histedit.sh -q -r "/tmp"
./integrate.sh -q -r "/tmp"
./stage.sh -q -r "/tmp"
--- /tmp/got-test-stage_patch-BSWyOGE0/stdout.expected	2021-09-23 08:56:16.099804826 +0200
+++ /tmp/got-test-stage_patch-BSWyOGE0/stdout	2021-09-23 08:56:16.087804754 +0200
@@ -1,34 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-stage this change? [y/n/q] n
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-stage this change? [y/n/q] n
------------------------------------------------
-@@ -13,4 +13,4 @@
- 13
- 14
- 15
--16
-+c
------------------------------------------------
-M  numbers (change 3 of 3)
-stage this change? [y/n/q] n
test failed; leaving test data in /tmp/got-test-stage_patch-BSWyOGE0
--- /tmp/got-test-stage_patch_twice-BsykdfRn/stdout.expected	2021-09-23 08:56:16.166805226 +0200
+++ /tmp/got-test-stage_patch_twice-BsykdfRn/stdout	2021-09-23 08:56:16.154805154 +0200
@@ -1,34 +0,0 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-stage this change? [y/n/q] n
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-stage this change? [y/n/q] y
------------------------------------------------
-@@ -13,4 +13,4 @@
- 13
- 14
- 15
--16
-+c
------------------------------------------------
-M  numbers (change 3 of 3)
-stage this change? [y/n/q] n
test failed; leaving test data in /tmp/got-test-stage_patch_twice-BsykdfRn
--- /tmp/got-test-stage_patch_quit-J0juWz57/stdout.expected	2021-09-23 08:56:16.600807815 +0200
+++ /tmp/got-test-stage_patch_quit-J0juWz57/stdout	2021-09-23 08:56:16.598807803 +0200
@@ -1,26 +1,2 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-stage this change? [y/n/q] y
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-stage this change? [y/n/q] q
 D  zzz
-stage this deletion? [y/n] n
+stage this deletion? [y/n] y
test failed; leaving test data in /tmp/got-test-stage_patch_quit-J0juWz57
--- /tmp/got-test-stage_incomplete_script-HVP5w0t7/stderr.expected	2021-09-23 08:56:16.665808203 +0200
+++ /tmp/got-test-stage_incomplete_script-HVP5w0t7/stderr	2021-09-23 08:56:16.663808191 +0200
@@ -1 +1 @@
-got: invalid patch choice
+got: no changes to stage
test failed; leaving test data in /tmp/got-test-stage_incomplete_script-HVP5w0t7
./unstage.sh -q -r "/tmp"
test failed; leaving test data in /tmp/got-test-unstage_patch-AbHi9Neh
--- /tmp/got-test-unstage_patch_quit-OjUHD79o/stdout.expected	2021-09-23 08:56:17.960815928 +0200
+++ /tmp/got-test-unstage_patch_quit-OjUHD79o/stdout	2021-09-23 08:56:17.958815916 +0200
@@ -1,27 +1,3 @@
------------------------------------------------
-@@ -1,5 +1,5 @@
- 1
--2
-+a
- 3
- 4
- 5
------------------------------------------------
-M  numbers (change 1 of 3)
-unstage this change? [y/n/q] y
------------------------------------------------
-@@ -4,7 +4,7 @@
- 4
- 5
- 6
--7
-+b
- 8
- 9
- 10
------------------------------------------------
-M  numbers (change 2 of 3)
-unstage this change? [y/n/q] q
-G  numbers
 D  zzz
-unstage this deletion? [y/n] n
+unstage this deletion? [y/n] y
+D  zzz
test failed; leaving test data in /tmp/got-test-unstage_patch_quit-OjUHD79o
./cat.sh -q -r "/tmp"
./clone.sh -q -r "/tmp"
./fetch.sh -q -r "/tmp"
./tree.sh -q -r "/tmp"
./pack.sh -q -r "/tmp"
./cleanup.sh -q -r "/tmp"
make[1]: Leaving directory '/home/op/got-portable/regress/cmdline'