...

Source file src/github.com/tetratelabs/wazero/internal/gojs/testdata/writefs/main.go

Documentation: github.com/tetratelabs/wazero/internal/gojs/testdata/writefs

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

View as plain text