for portability, handle errno variations upon open(2) failure with O_NOFOLLOW Problem pointed out by naddy for FreeBSD -portable. Discussed with millert, thomas adam, and naddy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
diff --git a/got/got.c b/got/got.c
index 66b864f..0bf2f90 100644
--- a/got/got.c
+++ b/got/got.c
@@ -4394,7 +4394,7 @@ print_diff(void *arg, unsigned char status, unsigned char staged_status,
if (dirfd != -1) {
fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
- if (errno != ELOOP) {
+ if (!got_err_open_nofollow_on_symlink()) {
err = got_error_from_errno2("openat",
abspath);
goto done;
@@ -4407,7 +4407,7 @@ print_diff(void *arg, unsigned char status, unsigned char staged_status,
} else {
fd = open(abspath, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
- if (errno != ELOOP) {
+ if (!got_err_open_nofollow_on_symlink()) {
err = got_error_from_errno2("open",
abspath);
goto done;
diff --git a/include/got_error.h b/include/got_error.h
index f87c06c..e1becc0 100644
--- a/include/got_error.h
+++ b/include/got_error.h
@@ -427,3 +427,9 @@ const struct got_error *got_error_path(const char *, int);
* additional arguments.
*/
const struct got_error *got_error_fmt(int, const char *, ...);
+
+/*
+ * Check whether open(2) with O_NOFOLLOW failed on a symlink.
+ * This must be called directly after open(2) because it uses errno!
+ */
+int got_err_open_nofollow_on_symlink(void);
diff --git a/lib/error.c b/lib/error.c
index 72e0048..c4605e6 100644
--- a/lib/error.c
+++ b/lib/error.c
@@ -258,3 +258,22 @@ got_error_fmt(int code, const char *fmt, ...)
abort();
}
+
+int
+got_err_open_nofollow_on_symlink(void)
+{
+ /*
+ * Check whether open(2) with O_NOFOLLOW failed on a symlink.
+ * Posix mandates ELOOP and OpenBSD follows it. Others return
+ * different error codes. We carry this workaround to help the
+ * portable version a little.
+ */
+ return (errno == ELOOP
+#ifdef EMLINK
+ || errno == EMLINK
+#endif
+#ifdef EFTYPE
+ || errno == EFTYPE
+#endif
+ );
+}
diff --git a/lib/object_create.c b/lib/object_create.c
index d2c6246..1092bb0 100644
--- a/lib/object_create.c
+++ b/lib/object_create.c
@@ -129,7 +129,7 @@ got_object_blob_file_create(struct got_object_id **id, FILE **blobfile,
fd = open(ondisk_path, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
- if (errno != ELOOP)
+ if (!got_err_open_nofollow_on_symlink())
return got_error_from_errno2("open", ondisk_path);
if (lstat(ondisk_path, &sb) == -1) {
diff --git a/lib/worktree.c b/lib/worktree.c
index 904fb23..0c6b89c 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -1284,7 +1284,7 @@ replace_existing_symlink(int *did_something, const char *ondisk_path,
*/
fd = open(ondisk_path, O_RDWR | O_EXCL | O_NOFOLLOW);
if (fd == -1) {
- if (errno != ELOOP)
+ if (!got_err_open_nofollow_on_symlink())
return got_error_from_errno2("open", ondisk_path);
/* We are updating an existing on-disk symlink. */
@@ -1781,9 +1781,10 @@ get_file_status(unsigned char *status, struct stat *sb,
}
} else {
fd = open(abspath, O_RDONLY | O_NOFOLLOW);
- if (fd == -1 && errno != ENOENT && errno != ELOOP)
+ if (fd == -1 && errno != ENOENT &&
+ !got_err_open_nofollow_on_symlink())
return got_error_from_errno2("open", abspath);
- else if (fd == -1 && errno == ELOOP) {
+ else if (fd == -1 && got_err_open_nofollow_on_symlink()) {
if (lstat(abspath, sb) == -1)
return got_error_from_errno2("lstat", abspath);
} else if (fd == -1 || fstat(fd, sb) == -1) {
@@ -3767,7 +3768,7 @@ worktree_status(struct got_worktree *worktree, const char *path,
fd = open(ondisk_path, O_RDONLY | O_NOFOLLOW | O_DIRECTORY);
if (fd == -1) {
if (errno != ENOTDIR && errno != ENOENT && errno != EACCES &&
- errno != ELOOP)
+ !got_err_open_nofollow_on_symlink())
err = got_error_from_errno2("open", ondisk_path);
else {
if (!no_ignores) {
@@ -4473,7 +4474,7 @@ create_patched_content(char **path_outfile, int reverse_patch,
if (dirfd2 != -1) {
fd2 = openat(dirfd2, de_name2, O_RDONLY | O_NOFOLLOW);
if (fd2 == -1) {
- if (errno != ELOOP) {
+ if (!got_err_open_nofollow_on_symlink()) {
err = got_error_from_errno2("openat", path2);
goto done;
}
@@ -4487,7 +4488,7 @@ create_patched_content(char **path_outfile, int reverse_patch,
} else {
fd2 = open(path2, O_RDONLY | O_NOFOLLOW);
if (fd2 == -1) {
- if (errno != ELOOP) {
+ if (!got_err_open_nofollow_on_symlink()) {
err = got_error_from_errno2("open", path2);
goto done;
}