Subject: [PATCH] pg_restore: use strerror for errors other than ENOENT

From: Justin Pryzby <[email-masked]>
Date: Sat, 14 Jan 2023 20:16:28 -0600

Without this patch:

$ mkdir 000; chmod 000 ./000
$ strace -fe open,stat ./tmp_install/usr/local/pgsql/bin/pg_restore -vvv -l ./000
...
pg_restore: allocating AH for ./000, format 0
pg_restore: attempting to ascertain archive format
stat("./000", {st_mode=S_IFDIR|000, st_size=4096, ...}) = 0
stat("./000/toc.dat", 0x7ffc679fb3a0)   = -1 EACCES (Permission denied)
stat("./000/toc.dat.gz", 0x7ffc679fb3a0) = -1 EACCES (Permission denied)
pg_restore: error: directory "./000" does not appear to be a valid archive ("toc.dat" does not exist)
+++ exited with 1 +++

With:
stat("./000/toc.dat", 0x7ffc29ad0eb0)   = -1 EACCES (Permission denied)
pg_restore: error: could not open input file "./000": Permission denied

I "learned" some time ago to infer what the error message should have
said, and finally wrote this so it does what it should.  I'd consider
this a backpatchable fix to save people the trouble of diagnosing the
meaning of the error message.

commit 45722852d98b8e9c2702aabe2745d7df200da124
Author: Justin Pryzby <[email-masked]>
Date:   Sat Jan 14 19:51:31 2023 -0600

    pg_restore: use strerror for errors other than ENOENT

diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 7f7a0f1ce7b..e36ee7af157 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -2107,7 +2107,12 @@ _discoverArchiveFormat(ArchiveHandle *AH)
 			if (snprintf(buf, MAXPGPATH, "%s/toc.dat", AH->fSpec) >= MAXPGPATH)
 				pg_fatal("directory name too long: \"%s\"",
 						 AH->fSpec);
-			if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
+			if (stat(buf, &st) != 0)
+			{
+				if (errno != ENOENT)
+					pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
+			}
+			else if (S_ISREG(st.st_mode))
 			{
 				AH->format = archDirectory;
 				return AH->format;
@@ -2117,7 +2122,12 @@ _discoverArchiveFormat(ArchiveHandle *AH)
 			if (snprintf(buf, MAXPGPATH, "%s/toc.dat.gz", AH->fSpec) >= MAXPGPATH)
 				pg_fatal("directory name too long: \"%s\"",
 						 AH->fSpec);
-			if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
+			if (stat(buf, &st) != 0)
+			{
+				if (errno != ENOENT)
+					pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
+			}
+			else if (S_ISREG(st.st_mode))
 			{
 				AH->format = archDirectory;
 				return AH->format;