github.com/tetratelabs/wazero@v1.2.1/internal/sysfs/futimens_test.go (about) 1 package sysfs 2 3 import ( 4 "os" 5 "path" 6 "runtime" 7 "syscall" 8 "testing" 9 "time" 10 11 "github.com/tetratelabs/wazero/internal/platform" 12 "github.com/tetratelabs/wazero/internal/testing/require" 13 ) 14 15 func TestUtimens(t *testing.T) { 16 t.Run("doesn't exist", func(t *testing.T) { 17 err := Utimens("nope", nil, true) 18 require.EqualErrno(t, syscall.ENOENT, err) 19 20 err = Utimens("nope", nil, false) 21 if SupportsSymlinkNoFollow { 22 require.EqualErrno(t, syscall.ENOENT, err) 23 } else { 24 require.EqualErrno(t, syscall.ENOSYS, err) 25 } 26 }) 27 testUtimens(t, false) 28 } 29 30 func testUtimens(t *testing.T, futimes bool) { 31 // Note: This sets microsecond granularity because Windows doesn't support 32 // nanosecond. 33 // 34 // Negative isn't tested as most platforms don't return consistent results. 35 tests := []struct { 36 name string 37 times *[2]syscall.Timespec 38 }{ 39 { 40 name: "nil", 41 }, 42 { 43 name: "a=omit,m=omit", 44 times: &[2]syscall.Timespec{ 45 {Sec: 123, Nsec: UTIME_OMIT}, 46 {Sec: 123, Nsec: UTIME_OMIT}, 47 }, 48 }, 49 { 50 name: "a=now,m=omit", 51 times: &[2]syscall.Timespec{ 52 {Sec: 123, Nsec: UTIME_NOW}, 53 {Sec: 123, Nsec: UTIME_OMIT}, 54 }, 55 }, 56 { 57 name: "a=omit,m=now", 58 times: &[2]syscall.Timespec{ 59 {Sec: 123, Nsec: UTIME_OMIT}, 60 {Sec: 123, Nsec: UTIME_NOW}, 61 }, 62 }, 63 { 64 name: "a=now,m=now", 65 times: &[2]syscall.Timespec{ 66 {Sec: 123, Nsec: UTIME_NOW}, 67 {Sec: 123, Nsec: UTIME_NOW}, 68 }, 69 }, 70 { 71 name: "a=now,m=set", 72 times: &[2]syscall.Timespec{ 73 {Sec: 123, Nsec: UTIME_NOW}, 74 {Sec: 123, Nsec: 4 * 1e3}, 75 }, 76 }, 77 { 78 name: "a=set,m=now", 79 times: &[2]syscall.Timespec{ 80 {Sec: 123, Nsec: 4 * 1e3}, 81 {Sec: 123, Nsec: UTIME_NOW}, 82 }, 83 }, 84 { 85 name: "a=set,m=omit", 86 times: &[2]syscall.Timespec{ 87 {Sec: 123, Nsec: 4 * 1e3}, 88 {Sec: 123, Nsec: UTIME_OMIT}, 89 }, 90 }, 91 { 92 name: "a=omit,m=set", 93 times: &[2]syscall.Timespec{ 94 {Sec: 123, Nsec: UTIME_OMIT}, 95 {Sec: 123, Nsec: 4 * 1e3}, 96 }, 97 }, 98 { 99 name: "a=set,m=set", 100 times: &[2]syscall.Timespec{ 101 {Sec: 123, Nsec: 4 * 1e3}, 102 {Sec: 223, Nsec: 5 * 1e3}, 103 }, 104 }, 105 } 106 for _, fileType := range []string{"dir", "file", "link", "link-follow"} { 107 for _, tt := range tests { 108 tc := tt 109 fileType := fileType 110 name := fileType + " " + tc.name 111 symlinkNoFollow := fileType == "link" 112 113 // symlinkNoFollow is invalid for file descriptor based operations, 114 // because the default for open is to follow links. You can't avoid 115 // this. O_NOFOLLOW is used only to return ELOOP on a link. 116 if futimes && symlinkNoFollow { 117 continue 118 } 119 120 t.Run(name, func(t *testing.T) { 121 tmpDir := t.TempDir() 122 file := path.Join(tmpDir, "file") 123 err := os.WriteFile(file, []byte{}, 0o700) 124 require.NoError(t, err) 125 126 link := file + "-link" 127 require.NoError(t, os.Symlink(file, link)) 128 129 dir := path.Join(tmpDir, "dir") 130 err = os.Mkdir(dir, 0o700) 131 require.NoError(t, err) 132 133 var path, statPath string 134 switch fileType { 135 case "dir": 136 path = dir 137 statPath = dir 138 case "file": 139 path = file 140 statPath = file 141 case "link": 142 path = link 143 statPath = link 144 case "link-follow": 145 path = link 146 statPath = file 147 default: 148 panic(tc) 149 } 150 151 oldSt, errno := lstat(statPath) 152 require.EqualErrno(t, 0, errno) 153 154 if !futimes { 155 err = Utimens(path, tc.times, !symlinkNoFollow) 156 if symlinkNoFollow && !SupportsSymlinkNoFollow { 157 require.EqualErrno(t, syscall.ENOSYS, err) 158 return 159 } 160 require.EqualErrno(t, 0, errno) 161 } else { 162 flag := syscall.O_RDWR 163 if path == dir { 164 flag = syscall.O_RDONLY 165 if runtime.GOOS == "windows" { 166 // windows requires O_RDWR, which is invalid for directories 167 t.Skip("windows cannot update timestamps on a dir") 168 } 169 } 170 171 f := requireOpenFile(t, path, flag, 0) 172 173 errno = f.Utimens(tc.times) 174 require.EqualErrno(t, 0, f.Close()) 175 require.EqualErrno(t, 0, errno) 176 } 177 178 newSt, errno := lstat(statPath) 179 require.EqualErrno(t, 0, errno) 180 181 if platform.CompilerSupported() { 182 if tc.times != nil && tc.times[0].Nsec == UTIME_OMIT { 183 require.Equal(t, oldSt.Atim, newSt.Atim) 184 } else if tc.times == nil || tc.times[0].Nsec == UTIME_NOW { 185 now := time.Now().UnixNano() 186 require.True(t, newSt.Atim <= now, "expected atim %d <= now %d", newSt.Atim, now) 187 } else { 188 require.Equal(t, tc.times[0].Nano(), newSt.Atim) 189 } 190 } 191 192 // When compiler isn't supported, we can still check mtim. 193 if tc.times != nil && tc.times[1].Nsec == UTIME_OMIT { 194 require.Equal(t, oldSt.Mtim, newSt.Mtim) 195 } else if tc.times == nil || tc.times[1].Nsec == UTIME_NOW { 196 now := time.Now().UnixNano() 197 require.True(t, newSt.Mtim <= now, "expected mtim %d <= now %d", newSt.Mtim, now) 198 } else { 199 require.Equal(t, tc.times[1].Nano(), newSt.Mtim) 200 } 201 }) 202 } 203 } 204 }