github.com/tetratelabs/wazero@v1.2.1/internal/gojs/testdata/writefs/main.go (about) 1 package writefs 2 3 import ( 4 "errors" 5 "fmt" 6 "io/fs" 7 "log" 8 "os" 9 "path" 10 "syscall" 11 "time" 12 ) 13 14 func Main() { 15 // Create a test directory 16 dir := path.Join(os.TempDir(), "dir") 17 dir1 := path.Join(os.TempDir(), "dir1") 18 if err := os.Mkdir(dir, 0o700); err != nil { 19 log.Panicln(err) 20 return 21 } 22 defer os.Remove(dir) 23 24 // Create a test file in that directory 25 file := path.Join(dir, "file") 26 file1 := path.Join(os.TempDir(), "file1") 27 if err := os.WriteFile(file, []byte{}, 0o600); err != nil { 28 log.Panicln(err) 29 return 30 } 31 defer os.Remove(file) 32 33 // Ensure stat works, particularly mode. 34 for _, path := range []string{dir, file} { 35 if stat, err := os.Stat(path); err != nil { 36 log.Panicln(err) 37 } else { 38 fmt.Println(path, "mode", stat.Mode()) 39 } 40 } 41 42 // Now, test that syscall.WriteAt works 43 f, err := os.OpenFile(file1, os.O_RDWR|os.O_CREATE, 0o600) 44 if err != nil { 45 log.Panicln(err) 46 } 47 defer f.Close() 48 49 // Write segments to the file, noting map iteration isn't ordered. 50 bytes := []byte("wazero") 51 for o, b := range map[int][]byte{3: bytes[3:], 0: bytes[:3]} { 52 n, err := f.WriteAt(b, int64(o)) 53 if err != nil { 54 log.Panicln(err) 55 } else if n != 3 { 56 log.Panicln("expected 3, but wrote", n) 57 } 58 } 59 60 // Now, use ReadAt (tested in testfs package) to verify everything wrote! 61 if _, err = f.ReadAt(bytes, 0); err != nil { 62 log.Panicln(err) 63 } else if string(bytes) != "wazero" { 64 log.Panicln("unexpected contents:", string(bytes)) 65 } 66 67 // Next, truncate it. 68 if err = f.Truncate(2); err != nil { 69 log.Panicln(err) 70 } 71 // Next, sync it. 72 if err = f.Sync(); err != nil { 73 log.Panicln(err) 74 } 75 // Next, chmod it (tests Fchmod) 76 if err = f.Chmod(0o400); err != nil { 77 log.Panicln(err) 78 } 79 if stat, err := f.Stat(); err != nil { 80 log.Panicln(err) 81 } else if mode := stat.Mode() & fs.ModePerm; mode != 0o400 { 82 log.Panicln("expected mode = 0o400", mode) 83 } 84 85 // Invoke Chown in a way nothing changes, to ensure it doesn't panic. 86 if err = f.Chown(-1, -1); err != nil { 87 log.Panicln(err) 88 } 89 90 // Finally, close it. 91 if err = f.Close(); err != nil { 92 log.Panicln(err) 93 } 94 95 // Revert to writeable 96 if err = syscall.Chmod(file1, 0o600); err != nil { 97 log.Panicln(err) 98 } 99 100 // Test stat 101 stat, err := os.Stat(file1) 102 if err != nil { 103 log.Panicln(err) 104 } 105 106 // Invoke Chown in a way nothing changes, to ensure it doesn't panic. 107 if err = os.Chown(file1, -1, -1); err != nil { 108 log.Panicln(err) 109 } 110 111 if stat.Mode().Type() != 0 { 112 log.Panicln("expected type = 0", stat.Mode().Type()) 113 } 114 if stat.Mode().Perm() != 0o600 { 115 log.Panicln("expected perm = 0o600", stat.Mode().Perm()) 116 } 117 118 // Check the file was truncated. 119 if bytes, err := os.ReadFile(file1); err != nil { 120 log.Panicln(err) 121 } else if string(bytes) != "wa" { 122 log.Panicln("unexpected contents:", string(bytes)) 123 } 124 125 // Now, truncate it by path 126 if err = os.Truncate(file1, 1); err != nil { 127 log.Panicln(err) 128 } else if bytes, err := os.ReadFile(file1); err != nil { 129 log.Panicln(err) 130 } else if string(bytes) != "w" { 131 log.Panicln("unexpected contents:", string(bytes)) 132 } 133 134 // create a hard link 135 link := file1 + "-link" 136 if err = os.Link(file1, link); err != nil { 137 log.Panicln(err) 138 } 139 140 // Ensure this is a hard link, so they have the same inode. 141 file1Stat, err := os.Lstat(file1) 142 if err != nil { 143 log.Panicln(err) 144 } 145 linkStat, err := os.Lstat(link) 146 if err != nil { 147 log.Panicln(err) 148 } 149 if !os.SameFile(file1Stat, linkStat) { 150 log.Panicln("expected file == link stat", file1Stat, linkStat) 151 } 152 153 // create a symbolic link 154 symlink := file1 + "-symlink" 155 if err = os.Symlink(file1, symlink); err != nil { 156 log.Panicln(err) 157 } 158 159 // verify we can read the symbolic link back 160 if dst, err := os.Readlink(symlink); err != nil { 161 log.Panicln(err) 162 } else if dst != dst { 163 log.Panicln("expected link dst = old value", dst, dst) 164 } 165 166 // Test lstat which should be about the link not its target. 167 symlinkStat, err := os.Lstat(symlink) 168 if err != nil { 169 log.Panicln(err) 170 } 171 172 if symlinkStat.Mode().Type() != fs.ModeSymlink { 173 log.Panicln("expected type = symlink", symlinkStat.Mode().Type()) 174 } 175 if size := int64(len(file1)); symlinkStat.Size() != size { 176 log.Panicln("unexpected symlink size", symlinkStat.Size(), size) 177 } 178 // A symbolic link isn't the same file as what it points to. 179 if os.SameFile(file1Stat, symlinkStat) { 180 log.Panicln("expected file != link stat", file1Stat, symlinkStat) 181 } 182 183 // Invoke Lchown in a way nothing changes, to ensure it doesn't panic. 184 if err = os.Lchown(symlink, -1, -1); err != nil { 185 log.Panicln(err) 186 } 187 188 // Test removing a non-empty empty directory 189 if err = syscall.Rmdir(dir); err != syscall.ENOTEMPTY { 190 log.Panicln("unexpected error", err) 191 } 192 193 // Test updating the mod time of a file, noting JS has millis precision. 194 atime := time.Unix(123, 4*1e6) 195 mtime := time.Unix(567, 8*1e6) 196 197 // Ensure errors propagate 198 if err = os.Chtimes("noexist", atime, mtime); !errors.Is(err, syscall.ENOENT) { 199 log.Panicln("unexpected error", err) 200 } 201 202 // Now, try a real update. 203 if err = os.Chtimes(dir, atime, mtime); err != nil { 204 log.Panicln("unexpected error", err) 205 } 206 207 // Ensure the times translated properly. 208 dirAtimeNsec, dirMtimeNsec, dirDev, dirInode := statFields(dir) 209 fmt.Println("dir times:", dirAtimeNsec, dirMtimeNsec) 210 211 // Ensure we were able to read the dev and inode. 212 // 213 // Note: The size of syscall.Stat_t.Dev (32-bit) in js is smaller than 214 // linux (64-bit), so we can't compare its real value against the host. 215 if dirDev == 0 { 216 log.Panicln("expected dir dev != 0", dirDev) 217 } 218 if dirInode == 0 { 219 log.Panicln("expected dir inode != 0", dirInode) 220 } 221 222 // Test renaming a file, noting we can't verify error numbers as they 223 // vary per operating system. 224 if err = syscall.Rename(file, dir); err == nil { 225 log.Panicln("expected error") 226 } 227 if err = syscall.Rename(file, file1); err != nil { 228 log.Panicln("unexpected error", err) 229 } 230 231 // Test renaming a directory 232 if err = syscall.Rename(dir, file1); err == nil { 233 log.Panicln("expected error") 234 } 235 if err = syscall.Rename(dir, dir1); err != nil { 236 log.Panicln("unexpected error", err) 237 } 238 239 // Compare stat after renaming. 240 atimeNsec, mtimeNsec, dev, inode := statFields(dir1) 241 // atime shouldn't change as we didn't access (re-open) the directory. 242 if atimeNsec != dirAtimeNsec { 243 log.Panicln("expected dir atimeNsec = previous value", atimeNsec, dirAtimeNsec) 244 } 245 // mtime should change because we renamed the directory. 246 if mtimeNsec <= dirMtimeNsec { 247 log.Panicln("expected dir mtimeNsec > previous value", mtimeNsec, dirMtimeNsec) 248 } 249 // dev/inode shouldn't change during rename. 250 if dev != dirDev { 251 log.Panicln("expected dir dev = previous value", dev, dirDev) 252 } 253 if inode != dirInode { 254 log.Panicln("expected dir inode = previous value", dev, dirInode) 255 } 256 257 // Test unlinking a file 258 if err = syscall.Rmdir(file1); err != syscall.ENOTDIR { 259 log.Panicln("unexpected error", err) 260 } 261 if err = syscall.Unlink(file1); err != nil { 262 log.Panicln("unexpected error", err) 263 } 264 265 // Test removing an empty directory 266 if err = syscall.Unlink(dir1); err != syscall.EISDIR { 267 log.Panicln("unexpected error", err) 268 } 269 if err = syscall.Rmdir(dir1); err != nil { 270 log.Panicln("unexpected error", err) 271 } 272 273 // shouldn't fail 274 if err = os.RemoveAll(dir1); err != nil { 275 log.Panicln(err) 276 return 277 } 278 279 // ensure we can use zero as is used in TestRemoveReadOnlyDir 280 if err = os.Mkdir(dir1, 0); err != nil { 281 log.Panicln(err) 282 return 283 } 284 defer os.Remove(dir) 285 286 // Symlink and Readlink tests. 287 s := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" 288 from := "/symlink.txt" 289 err = os.Symlink(s, from) 290 if err != nil { 291 log.Panicln(err) 292 } 293 294 r, err := os.Readlink(from) 295 if err != nil { 296 log.Fatalf("readlink %q failed: %v", from, err) 297 } 298 if r != s { 299 log.Fatalf("after symlink %q != %q", r, s) 300 } 301 }