Repository: fsnotify/fsnotify
Pull request #530 (2022-10-20 00:47:17)

Check privileges for symlink tests on Windows
Tests for symlink always fail when run the tests as non-admin users on Windows since Windows require privileges to create symlinks. This CL change to check the privileges.

(Can I put label `hacktoberfest-accepted` on this PR ?)

arp242: Can you put the `helper_.go` files in the `internal` package? There's already `internal/windows.go` and `internal/unix.go`, although the unix.go one has odd build tags; maybe add a unix2.go.

Just keeps the root a bit cleaner of files 😅
mattn: Thanks.
mattn: Ah, newer Go fmt breaks indentation.
arp242: > Ah, newer Go fmt breaks indentation.

I guess I still use an older `goimports` that I haven't updated since 1.19. Bit annoying it reformats it; I like to indent my TODO comments like that, but can remove the indentation if it's causing gofmt problems.
mattn: I've had too much coffee and I'm full. ☕😂

![image](https://user-images.githubusercontent.com/10111/196840006-7b66c532-a8b1-400c-8e18-13bc26500be7.png)


Patch:
diff --git a/internal/unix2.go b/internal/unix2.go
new file mode 100644
index 0000000..dc40459
--- /dev/null
+++ b/internal/unix2.go
@@ -0,0 +1,8 @@
+//go:build !windows
+// +build !windows
+
+package internal
+
+func HasPrivilegesForSymlink() bool {
+	return true
+}
diff --git a/internal/windows.go b/internal/windows.go
index 0e9b3b2..62d1d17 100644
--- a/internal/windows.go
+++ b/internal/windows.go
@@ -5,6 +5,8 @@ package internal
 
 import (
 	"errors"
+
+	"golang.org/x/sys/windows"
 )
 
 // Just a dummy.
@@ -17,3 +19,24 @@ func SetRlimit()                                    {}
 func Maxfiles() uint64                              { return 1<<64 - 1 }
 func Mkfifo(path string, mode uint32) error         { return errors.New("no FIFOs on Windows") }
 func Mknod(path string, mode uint32, dev int) error { return errors.New("no device nodes on Windows") }
+
+func HasPrivilegesForSymlink() bool {
+	var sid *windows.SID
+	err := windows.AllocateAndInitializeSid(
+		&windows.SECURITY_NT_AUTHORITY,
+		2,
+		windows.SECURITY_BUILTIN_DOMAIN_RID,
+		windows.DOMAIN_ALIAS_RID_ADMINS,
+		0, 0, 0, 0, 0, 0,
+		&sid)
+	if err != nil {
+		return false
+	}
+	defer windows.FreeSid(sid)
+	token := windows.Token(0)
+	member, err := token.IsMember(sid)
+	if err != nil {
+		return false
+	}
+	return member || token.IsElevated()
+}


Test patch:
diff --git a/fsnotify_test.go b/fsnotify_test.go
index 16431f5..beb2c62 100644
--- a/fsnotify_test.go
+++ b/fsnotify_test.go
@@ -166,6 +166,9 @@ func TestWatch(t *testing.T) {
 				// behaviour too.
 				t.Skip("broken on macOS")
 			}
+			if !internal.HasPrivilegesForSymlink() {
+				t.Skip("does not have privileges for symlink on this OS")
+			}
 
 			file := join(tmp, "file")
 			link := join(tmp, "link")
@@ -195,6 +198,9 @@ func TestWatch(t *testing.T) {
 				// behaviour too.
 				t.Skip("broken on macOS")
 			}
+			if !internal.HasPrivilegesForSymlink() {
+				t.Skip("does not have privileges for symlink on this OS")
+			}
 
 			dir := join(tmp, "dir")
 			link := join(tmp, "link")
@@ -246,6 +252,9 @@ func TestWatchCreate(t *testing.T) {
 
 		// Links
 		{"create new symlink to file", func(t *testing.T, w *Watcher, tmp string) {
+			if !internal.HasPrivilegesForSymlink() {
+				t.Skip("does not have privileges for symlink on this OS")
+			}
 			touch(t, tmp, "file")
 			addWatch(t, w, tmp)
 			symlink(t, join(tmp, "file"), tmp, "link")
@@ -253,6 +262,9 @@ func TestWatchCreate(t *testing.T) {
 			create  /link
 		`},
 		{"create new symlink to directory", func(t *testing.T, w *Watcher, tmp string) {
+			if !internal.HasPrivilegesForSymlink() {
+				t.Skip("does not have privileges for symlink on this OS")
+			}
 			addWatch(t, w, tmp)
 			symlink(t, tmp, tmp, "link")
 		}, `
@@ -505,6 +517,10 @@ func TestWatchRename(t *testing.T) {
 }
 
 func TestWatchSymlink(t *testing.T) {
+	if !internal.HasPrivilegesForSymlink() {
+		t.Skip("does not have privileges for symlink on this OS")
+	}
+
 	tests := []testCase{
 		{"create unresolvable symlink", func(t *testing.T, w *Watcher, tmp string) {
 			addWatch(t, w, tmp)