func (f *AzukiFile) Truncate(size uint64) fuse.Status { f.lock.Lock() r := fuse.ToStatus(syscall.Ftruncate(int(f.File.Fd()), int64(size))) f.lock.Unlock() go event.Notify(event.Trunc, f.File.Name()) return r }
func main() { flag.Var(&windowSize, "window-size", "Window size in Go `duration` format (default \"10m\")") flag.IntVar(&maxRestarts, "max-restarts", 5, "Max `restarts` within window-size duration") flag.StringVar(&metadataDir, "metadata-dir", "/run/runlimit", "Metadata `dir`, where metadata files are stored") flag.StringVar(&metadataKey, "metadata-key", "", "Metadata key, which will form part of the metadata file name") flag.StringVar(&svCmd, "sv-cmd", "", "Command to use to stop a service") flag.Parse() if windowSize == 0 || maxRestarts == 0 { fatal("-max-restarts and/or -window-size cannot be 0") } cmdline := flag.Args() if len(cmdline) < 1 { fatal("No command supplied") } cwd, err := os.Getwd() assert(err) if metadataKey == "" { metadataKey = nonalphanumeric.ReplaceAllString(cwd, "_") } metafile := filepath.Join(metadataDir, fmt.Sprintf("%s.meta", metadataKey)) f, err := os.OpenFile(metafile, os.O_RDWR|os.O_CREATE, os.FileMode(0644)) assert(err) defer f.Close() assert(syscall.Flock(int(f.Fd()), syscall.LOCK_NB|syscall.LOCK_EX)) metadata := &Metadata{} if err := json.NewDecoder(f).Decode(metadata); err != nil && err != io.EOF { warning("metadata corrupted, ignoring...") } if limit(metadata, time.Duration(windowSize), maxRestarts) { signals := make(chan os.Signal) signal.Notify(signals, syscall.SIGTERM) if svCmd != "" { parts, err := shlex.Split(svCmd, true) assert(err) go func() { if out, err := exec.Command(parts[0], parts[1:]...).Output(); err != nil { warning("command exited abnormally with output %s", string(out)) } }() select { case <-signals: break case <-time.After(5 * time.Second): warning("timed out while waiting for TERM from %s", parts[0]) } } fatal("max restart intensity reached") } assert(syscall.Ftruncate(int(f.Fd()), 0)) _, err = syscall.Seek(int(f.Fd()), 0, 0) assert(err) if err := json.NewEncoder(f).Encode(metadata); err != nil { warning("could not write metadata: %s", err.Error()) } assert(chainlib.Exec(cmdline, nil)) }
func (f *loopbackFile) Truncate(size uint64) fuse.Status { f.lock.Lock() r := fuse.ToStatus(syscall.Ftruncate(int(f.File.Fd()), int64(size))) f.lock.Unlock() return r }
func LoadShared(name string) (Memory, error) { // TODO: portable shm_open filename := filepath.Join("/dev/shm", name) if file, err := os.OpenFile( filename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600, ); err == nil { // new file defer file.Close() log.Printf( "Creating a shared memory object (%s) of size: %d bytes", filename, storageSize, ) // TODO: prevent other processes from reading until setup is done if err := syscall.Ftruncate( int(file.Fd()), int64(storageSize), ); err != nil { return nil, err } return initMemory(file) } file, err := os.OpenFile(filename, os.O_RDWR, 0600) if err != nil { return nil, err } defer file.Close() return loadMemory(file) }
// TruncateTo enlarges or shortens the file backing the // memory map to be size newSize bytes. It only impacts // the file underlying the mapping, not // the mapping itself at this point. func (mm *MmapMalloc) TruncateTo(newSize int64) { if mm.File == nil { panic("cannot call TruncateTo() on a non-file backed MmapMalloc.") } err := syscall.Ftruncate(int(mm.File.Fd()), newSize) if err != nil { panic(err) } }
func TestFSetAttr(t *testing.T) { fs := &FSetAttrFs{} dir := MakeTempDir() defer os.RemoveAll(dir) state, _, err := MountFileSystem(dir, fs, nil) CheckSuccess(err) state.Debug = true defer state.Unmount() go state.Loop(false) fn := dir + "/file" f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755) CheckSuccess(err) defer f.Close() _, err = f.WriteString("hello") CheckSuccess(err) fmt.Println("Ftruncate") code := syscall.Ftruncate(f.Fd(), 3) if code != 0 { t.Error("truncate retval", os.NewSyscallError("Ftruncate", code)) } if len(fs.file.data) != 3 { t.Error("truncate") } if state.KernelSettings().Flags&CAP_FILE_OPS == 0 { log.Println("Mount does not support file operations") m, _ := json.Marshal(state.KernelSettings()) log.Println("Kernel settings: ", string(m)) return } _, err = os.Lstat(fn) CheckSuccess(err) if !fs.file.GetAttrCalled { t.Error("Should have called File.GetAttr") } err = os.Chmod(fn, 024) CheckSuccess(err) if fs.file.FileInfo.Mode&07777 != 024 { t.Error("chmod") } err = os.Chtimes(fn, 100, 101) CheckSuccess(err) if fs.file.FileInfo.Atime_ns != 100 || fs.file.FileInfo.Atime_ns != 101 { t.Error("Utimens") } // TODO - test chown if run as root. }
// Truncate changes the size of the file. // It does not change the I/O offset. // If there is an error, it will be of type *PathError. func (f *File) Truncate(size int64) error { if f == nil { return ErrInvalid } if e := syscall.Ftruncate(f.fd, size); e != nil { return &PathError{"truncate", f.name, e} } return nil }
// Truncate changes the size of the file. // It does not change the I/O offset. // If there is an error, it will be of type *PathError. func (f *File) Truncate(size int64) error { if err := f.checkValid("truncate"); err != nil { return err } if e := syscall.Ftruncate(f.fd, size); e != nil { return &PathError{"truncate", f.name, e} } return nil }
func (o *ObjectFile) Truncate(size uint64) fuse.Status { log.Debugf("[objectfile] Truncate %s", o.name) o.lock.Lock() r := fuse.ToStatus(syscall.Ftruncate(int(o.localfile.Fd()), int64(size))) o.needUpload = true o.lock.Unlock() return r }
func TestFSetAttr(t *testing.T) { fs := &FSetAttrFs{} dir, clean, sync := setupFAttrTest(t, fs) defer clean() fn := dir + "/file" f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755) CheckSuccess(err) defer f.Close() fi, err := f.Stat() CheckSuccess(err) _, err = f.WriteString("hello") CheckSuccess(err) code := syscall.Ftruncate(int(f.Fd()), 3) if code != nil { t.Error("truncate retval", os.NewSyscallError("Ftruncate", code)) } sync() if len(fs.file.data) != 3 { t.Error("truncate") } err = f.Chmod(024) CheckSuccess(err) sync() if fs.file.Attr.Mode&07777 != 024 { t.Error("chmod") } err = os.Chtimes(fn, time.Unix(0, 100e3), time.Unix(0, 101e3)) CheckSuccess(err) sync() if fs.file.Attr.Atimensec != 100e3 || fs.file.Attr.Mtimensec != 101e3 { t.Errorf("Utimens: atime %d != 100e3 mtime %d != 101e3", fs.file.Attr.Atimensec, fs.file.Attr.Mtimensec) } newFi, err := f.Stat() CheckSuccess(err) i1 := ToStatT(fi).Ino i2 := ToStatT(newFi).Ino if i1 != i2 { t.Errorf("f.Lstat().Ino = %d. Returned %d before.", i2, i1) } // TODO - test chown if run as root. }
// TestFcntlFlock tests whether the file locking structure matches // the calling convention of each kernel. // On some Linux systems, glibc uses another set of values for the // commands and translates them to the correct value that the kernel // expects just before the actual fcntl syscall. As Go uses raw // syscalls directly, it must use the real value, not the glibc value. // Thus this test also verifies that the Flock_t structure can be // roundtripped with F_SETLK and F_GETLK. func TestFcntlFlock(t *testing.T) { if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { t.Skip("skipping; no child processes allowed on iOS") } flock := syscall.Flock_t{ Type: syscall.F_WRLCK, Start: 31415, Len: 271828, Whence: 1, } if os.Getenv("GO_WANT_HELPER_PROCESS") == "" { // parent name := filepath.Join(os.TempDir(), "TestFcntlFlock") fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0) if err != nil { t.Fatalf("Open failed: %v", err) } defer syscall.Unlink(name) defer syscall.Close(fd) if err := syscall.Ftruncate(fd, 1<<20); err != nil { t.Fatalf("Ftruncate(1<<20) failed: %v", err) } if err := syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &flock); err != nil { t.Fatalf("FcntlFlock(F_SETLK) failed: %v", err) } cmd := exec.Command(os.Args[0], "-test.run=^TestFcntlFlock$") cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") cmd.ExtraFiles = []*os.File{os.NewFile(uintptr(fd), name)} out, err := cmd.CombinedOutput() if len(out) > 0 || err != nil { t.Fatalf("child process: %q, %v", out, err) } } else { // child got := flock // make sure the child lock is conflicting with the parent lock got.Start-- got.Len++ if err := syscall.FcntlFlock(3, syscall.F_GETLK, &got); err != nil { t.Fatalf("FcntlFlock(F_GETLK) failed: %v", err) } flock.Pid = int32(syscall.Getppid()) // Linux kernel always set Whence to 0 flock.Whence = 0 if got.Type == flock.Type && got.Start == flock.Start && got.Len == flock.Len && got.Pid == flock.Pid && got.Whence == flock.Whence { os.Exit(0) } t.Fatalf("FcntlFlock got %v, want %v", got, flock) } }
func CreateAnonymousFile(size int) (*os.File, error) { template := "wayland-shared" dir := os.Getenv("XDG_RUNTIME_DIR") if dir == "" { panic("XDG_RUNTIME_DIR not defined.") } ret, err := ioutil.TempFile(dir, template) if err != nil { return nil, err } err = syscall.Ftruncate(int(ret.Fd()), int64(size)) if err != nil { return nil, err } return ret, nil }
// Do does what you want: it creates (or opens) a pidfile, exclusively locks it // and writes the current executing programs PID to it. It returns the file // descriptor and an error. func Do(filename string, permissions ...uint32) (*os.File, error) { if len(permissions) == 0 { permissions = []uint32{0666} } fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, os.FileMode(permissions[0])) if err != nil { return nil, err } err = syscall.Flock(int(fp.Fd()), syscall.LOCK_NB|syscall.LOCK_EX) if err != nil { return nil, err } syscall.Ftruncate(int(fp.Fd()), 0) syscall.Write(int(fp.Fd()), []byte(fmt.Sprintf("%d", os.Getpid()))) return fp, nil }
func (this *Mmap) checkFileCap(start, lens int64) error { if start+lens >= this.FileLen { err := syscall.Ftruncate(int(this.FileFd.Fd()), this.FileLen+APPEND_DATA) if err != nil { fmt.Printf("ftruncate error : %v\n", err) return err } this.FileLen += APPEND_DATA this.FilePointer = start + lens } return nil }
func (this *Detail) WriteUpDetailFile() error { fout, err := os.Create("./index/detail.up") defer fout.Close() if err != nil { //fmt.Printf("Create %v\n",file_name) return err } err = syscall.Ftruncate(int(fout.Fd()), utils.APPEND_DATA) if err != nil { fmt.Printf("ftruncate error : %v\n", err) return err } return nil }
func TestFSetAttr(t *testing.T) { fs := &FSetAttrFs{} dir, clean := setupFAttrTest(fs) defer clean() fn := dir + "/file" f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755) CheckSuccess(err) defer f.Close() fi, err := f.Stat() CheckSuccess(err) _, err = f.WriteString("hello") CheckSuccess(err) fmt.Println("Ftruncate") code := syscall.Ftruncate(f.Fd(), 3) if code != 0 { t.Error("truncate retval", os.NewSyscallError("Ftruncate", code)) } if len(fs.file.data) != 3 { t.Error("truncate") } err = f.Chmod(024) CheckSuccess(err) if fs.file.FileInfo.Mode&07777 != 024 { t.Error("chmod") } err = os.Chtimes(fn, 100e3, 101e3) CheckSuccess(err) if fs.file.FileInfo.Atime_ns != 100e3 || fs.file.FileInfo.Mtime_ns != 101e3 { t.Errorf("Utimens: atime %d != 100e3 mtime %d != 101e3", fs.file.FileInfo.Atime_ns, fs.file.FileInfo.Mtime_ns) } newFi, err := f.Stat() CheckSuccess(err) if fi.Ino != newFi.Ino { t.Errorf("f.Lstat().Ino = %d. Returned %d before.", newFi.Ino, fi.Ino) } // TODO - test chown if run as root. }
func (this *InvertIdx) WriteUpIndexFile() error { file_name := fmt.Sprintf("./index/%v.up", this.IdxName) fout, err := os.Create(file_name) defer fout.Close() if err != nil { //fmt.Printf("Create %v\n",file_name) return err } err = syscall.Ftruncate(int(fout.Fd()), APPEND_DATA) if err != nil { fmt.Printf("ftruncate error : %v\n", err) return err } return nil }
func (this *Mmap) checkFilePointer(check_value int64) error { if this.FilePointer+check_value >= this.FileLen { err := syscall.Ftruncate(int(this.FileFd.Fd()), this.FileLen+APPEND_DATA) if err != nil { fmt.Printf("ftruncate error : %v\n", err) return err } this.FileLen += APPEND_DATA syscall.Munmap(this.MmapBytes) this.MmapBytes, err = syscall.Mmap(int(this.FileFd.Fd()), 0, int(this.FileLen), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) if err != nil { fmt.Printf("MAPPING ERROR %v \n", err) return err } } return nil }
func (bt *BTreedb) checkmmap() error { if int(int64(bt.meta.maxpgid)*pagesize) >= len(bt.mmapbytes) { err := syscall.Ftruncate(int(bt.fd.Fd()), int64(bt.meta.maxpgid+1)*pagesize) if err != nil { fmt.Printf("ftruncate error : %v\n", err) return err } maxpgid := bt.meta.maxpgid syscall.Munmap(bt.mmapbytes) //fmt.Printf(".meta.maxpgid:%v\n",bt.meta.maxpgid) bt.mmapbytes, err = syscall.Mmap(int(bt.fd.Fd()), 0, int(int64( /*bt.meta.maxpgid*/ maxpgid+1)*pagesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) if err != nil { fmt.Printf("MAPPING ERROR %v \n", err) return err } bt.meta = (*metaInfo)(unsafe.Pointer(&bt.mmapbytes[0])) } return nil }
// LoadShared maps a shared memory region. Size must be consistent with all // others mapping the same region (i.e.: the same name). // // Loading a shared memory region using the wrong size can lead to segmentation // faults (SIGSEGV), or truncated data. func LoadShared(name string, size uint32) (SharedMemory, error) { file, err := shm.Open(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) var isNew bool if err == nil { isNew = true if err := syscall.Ftruncate( int(file.Fd()), int64(size), ); err != nil { return nil, err } } else { if file, err = shm.Open(name, os.O_RDWR, 0600); err != nil { return nil, err } } defer file.Close() data, err := syscall.Mmap(int(file.Fd()), 0, int(size), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED, ) if isNew { // clear all bits for i := range data { data[i] = 0 } } // size is in bytes, blocks have 4 bytes (uint32) blocksLen := size >> 2 // size / 4 if blocksLen == 0 { blocksLen = 1 // at least 1 block } blocks := *(*sliceType)(unsafe.Pointer(&data)) blocks.len = int(blocksLen) blocks.cap = int(blocksLen) return &sharedMemory{ blocks: *(*[]uint32)(unsafe.Pointer(&blocks)), raw: data, }, nil }
func MakeBitmapFile(indexname string) error { size := BitmapSize if size == 0 || size > BitmapSize { size = BitmapSize } else if remainder := size % 8; remainder != 0 { size += 8 - remainder } fout, err := os.Create(indexname) defer fout.Close() if err != nil { return err } err = syscall.Ftruncate(int(fout.Fd()), int64(size>>8)) if err != nil { fmt.Printf("ftruncate error : %v\n", err) return err } return nil }
// Appends a Wtmp entry to the wtmp file func (u *Utmp) UpdWtmp(path string) error { const utmpSize = unsafe.Sizeof(*u) var fileSize int64 file, lk, err := SafeOpen(path) if err != nil { goto done } fileSize, err = file.Seek(0, os.SEEK_END) if err != nil { // Cannot safely get file size in order to write goto done } // If we can't safely write, undo our changes and exit if fileSize%int64(utmpSize) != 0 { fileSize -= int64(utmpSize) terr := syscall.Ftruncate(int(file.Fd()), fileSize) if terr != nil { err = fmt.Errorf("database is an invalid size, truncate failed: %s", terr) } else { err = fmt.Errorf("database is an invalid size, rewound to %d", fileSize) } goto done } err = binary.Write(file, binary.LittleEndian, &u) done: if file != nil { SafeClose(file, lk) } return err }
func NewMmap(file_name string, mode int) (*Mmap, error) { this := &Mmap{MmapBytes: make([]byte, 0), FileName: file_name, FileLen: 0, MapType: 0, FilePointer: 0, FileFd: nil} file_mode := os.O_RDWR file_create_mode := os.O_RDWR | os.O_CREATE | os.O_TRUNC if mode == MODE_CREATE { file_mode = os.O_RDWR | os.O_CREATE | os.O_TRUNC } f, err := os.OpenFile(file_name, file_mode, 0664) if err != nil { f, err = os.OpenFile(file_name, file_create_mode, 0664) if err != nil { return nil, err } } fi, err := f.Stat() if err != nil { fmt.Printf("ERR:%v", err) } this.FileLen = fi.Size() if mode == MODE_CREATE || this.FileLen == 0 { syscall.Ftruncate(int(f.Fd()), fi.Size()+APPEND_DATA) this.FileLen = APPEND_DATA } this.MmapBytes, err = syscall.Mmap(int(f.Fd()), 0, int(this.FileLen), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) if err != nil { fmt.Printf("MAPPING ERROR %v \n", err) return nil, err } this.FileFd = f return this, nil }
// Writes to name at the appropriate place in the database func (u *Utmp) PutUtLine(file *os.File) error { const utmpSize = unsafe.Sizeof(*u) // Save current position _, cur := u.GetUtid(file) fileSize, err := file.Seek(0, os.SEEK_END) if err != nil { // Cannot safely get file size in order to write return err } // If we can't write safely undo our changes and exit if fileSize%int64(utmpSize) != 0 { fileSize -= int64(utmpSize) terr := syscall.Ftruncate(int(file.Fd()), fileSize) if terr != nil { err = fmt.Errorf("database is an invalid size, truncate failed: %s", terr) } else { err = fmt.Errorf("database is an invalid size, rewound to %d", fileSize) } return err } if cur == -1 { cur = fileSize } _, err = file.Seek(cur, os.SEEK_SET) if err != nil { return err } return binary.Write(file, binary.LittleEndian, u) }
func TestCanCreateAndUseSharedRegion(t *testing.T) { var ( expected = ([]byte)("a test") name = "shm-test-" + uuid.New() ) file, err := Open(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) if err != nil { t.Fatal(err) } defer func() { if err := file.Close(); err != nil { t.Error(err) } if err := Unlink(file.Name()); err != nil { t.Error(err) } }() if err := syscall.Ftruncate( int(file.Fd()), int64(len(expected)), ); err != nil { t.Fatal(err) } if _, err := file.Write(expected); err != nil { t.Fatal(err) } if err := file.Sync(); err != nil { t.Fatal(err) } buf := make([]byte, len(expected)) if _, err := file.ReadAt(buf, 0); err != nil { t.Fatal(err) } if string(buf) != string(expected) { t.Fatalf("Expected %q. Got: %q", string(expected), string(buf)) } }
func lockPidFile(pidfile string) error { fd, e := syscall.Open(pidfile, syscall.O_CREAT|syscall.O_RDWR, 0777) if e != nil { return e } e = syscall.Flock(fd, syscall.LOCK_NB|syscall.LOCK_EX) if e != nil { return e } e = syscall.Ftruncate(fd, 0) if e != nil { return e } _, e = syscall.Write(fd, []byte(fmt.Sprintf("%d", syscall.Getpid()))) if e != nil { return e } return nil }
// Creates a new region of shared memory by creating and mapping in a new // shm object. The returned region "owns" the shm object, and will mark it for // unlinking when the region is closed. func NewRegion(name string, length int) (result region, err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) fd, err := C.shm_open( cname, C.int(os.O_CREATE|os.O_EXCL|os.O_RDWR), 0600) if err != nil { return } file := os.NewFile(uintptr(fd), name) defer func() { if err != nil { file.Close() } }() err = syscall.Ftruncate(int(fd), int64(length)) if err != nil { return } buf, err := syscall.Mmap(int(fd), 0, length, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) if err != nil { return } result = region{ bytes: buf, fd: file, name: name, unlink: true} return }
func openMmap(f string, minLength int64) []byte { // will zero the bit fd, err := syscall.Open(f, syscall.O_RDWR|syscall.O_CREAT, 0666) if err != nil { log.Fatal(err) } var stat syscall.Stat_t syscall.Fstat(fd, &stat) size := stat.Size if size < minLength { syscall.Ftruncate(fd, minLength) size = minLength } data, err := syscall.Mmap(fd, 0, int(size), syscall.PROT_WRITE, syscall.MAP_SHARED) if err != nil { log.Fatal(err) } log.Println("open", f, "size:", size) syscall.Close(fd) return data }
// truncateGrowFile extends a file using seeking or ftruncate performing RMW on // the first and last block as necessary. New blocks in the middle become // file holes unless they have been fallocate()'d beforehand. func (f *file) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Status { if newPlainSz <= oldPlainSz { log.Panicf("BUG: newSize=%d <= oldSize=%d", newPlainSz, oldPlainSz) } var err error // File was empty, create new header if oldPlainSz == 0 { f.fileTableEntry.IDLock.Lock() _, err = f.createHeader() f.fileTableEntry.IDLock.Unlock() if err != nil { return fuse.ToStatus(err) } } // New blocks to add addBlocks := f.contentEnc.ExplodePlainRange(oldPlainSz, newPlainSz-oldPlainSz) if oldPlainSz > 0 && len(addBlocks) >= 2 { // Zero-pad the first block (unless the first block is also the last block) f.zeroPad(oldPlainSz) } lastBlock := addBlocks[len(addBlocks)-1] if lastBlock.IsPartial() { // Write at the new end of the file. The seek implicitly grows the file // (creates a file hole) and doWrite() takes care of RMW. off := lastBlock.BlockPlainOff() _, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip)) return status } off := lastBlock.BlockCipherOff() err = syscall.Ftruncate(f.intFd(), int64(off+f.contentEnc.CipherBS())) if err != nil { tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err) } return fuse.ToStatus(err) }
func TestFSetAttr(t *testing.T) { fs := pathfs.NewLockingFileSystem(&FSetAttrFs{ FileSystem: pathfs.NewDefaultFileSystem(), }) dir, clean := setupFAttrTest(t, fs) defer clean() fn := dir + "/file" f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755) if err != nil { t.Fatalf("OpenFile failed: %v", err) } defer f.Close() fi, err := f.Stat() if err != nil { t.Fatalf("Stat failed: %v", err) } _, err = f.WriteString("hello") if err != nil { t.Fatalf("WriteString failed: %v", err) } code := syscall.Ftruncate(int(f.Fd()), 3) if code != nil { t.Error("truncate retval", os.NewSyscallError("Ftruncate", code)) } if a, status := fs.GetAttr("file", nil); !status.Ok() { t.Fatalf("GetAttr: status %v", status) } else if a.Size != 3 { t.Errorf("truncate: size %d, status %v", a.Size, status) } if err := f.Chmod(024); err != nil { t.Fatalf("Chmod failed: %v", err) } if a, status := fs.GetAttr("file", nil); !status.Ok() { t.Errorf("chmod: %v", status) } else if a.Mode&07777 != 024 { t.Errorf("getattr after chmod: %o", a.Mode&0777) } if err := os.Chtimes(fn, time.Unix(0, 100e3), time.Unix(0, 101e3)); err != nil { t.Fatalf("Chtimes failed: %v", err) } if a, status := fs.GetAttr("file", nil); !status.Ok() { t.Errorf("GetAttr: %v", status) } else if a.Atimensec != 100e3 || a.Mtimensec != 101e3 { t.Errorf("Utimens: atime %d != 100e3 mtime %d != 101e3", a.Atimensec, a.Mtimensec) } newFi, err := f.Stat() if err != nil { t.Fatalf("Stat failed: %v", err) } i1 := fuse.ToStatT(fi).Ino i2 := fuse.ToStatT(newFi).Ino if i1 != i2 { t.Errorf("f.Lstat().Ino = %d. Returned %d before.", i2, i1) } // TODO - test chown if run as root. }