Example #1
2
func coalesce(parentFile *os.File, childFile *os.File) error {
	blockSize, err := getFileSystemBlockSize(childFile)
	if err != nil {
		panic("can't get FS block size, error: " + err.Error())
	}
	var data, hole int64
	for {
		data, err = syscall.Seek(int(childFile.Fd()), hole, seekData)
		if err != nil {
			// reaches EOF
			errno := err.(syscall.Errno)
			if errno == syscall.ENXIO {
				break
			} else {
				// unexpected errors
				log.Fatal("Failed to syscall.Seek SEEK_DATA")
				return err
			}
		}
		hole, err = syscall.Seek(int(childFile.Fd()), data, seekHole)
		if err != nil {
			log.Fatal("Failed to syscall.Seek SEEK_HOLE")
			return err
		}

		// now we have a data start offset and length(hole - data)
		// let's read from child and write to parent file block by block
		_, err = parentFile.Seek(data, os.SEEK_SET)
		if err != nil {
			log.Fatal("Failed to os.Seek os.SEEK_SET")
			return err
		}

		offset := data
		buffer := fio.AllocateAligned(blockSize)
		for offset != hole {
			// read a block from child, maybe use bufio or Reader stream
			n, err := fio.ReadAt(childFile, buffer, offset)
			if n != len(buffer) || err != nil {
				log.Fatal("Failed to read from childFile")
				return err
			}
			// write a block to parent
			n, err = fio.WriteAt(parentFile, buffer, offset)
			if n != len(buffer) || err != nil {
				log.Fatal("Failed to write to parentFile")
				return err
			}
			offset += int64(n)
		}
	}

	return nil
}
Example #2
0
func (k *PosixKernel) Lseek(fd co.Fd, offset co.Off, whence int) uint64 {
	off, err := syscall.Seek(int(fd), int64(offset), whence)
	if err != nil {
		return Errno(err)
	}
	return uint64(off)
}
Example #3
0
File: main.go Project: lwf/runlimit
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))
}
Example #4
0
// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {

	if DisableWritesForAppEngine && flag&(O_WRONLY|O_RDWR|O_CREATE|O_EXCL) != 0 {
		return nil, &PathError{"open", name, errDisabledWrites}
	}
	var (
		fd     int
		e      error
		create bool
		excl   bool
		trunc  bool
		append bool
	)

	if flag&O_CREATE == O_CREATE {
		flag = flag & ^O_CREATE
		create = true
	}
	if flag&O_EXCL == O_EXCL {
		excl = true
	}
	if flag&O_TRUNC == O_TRUNC {
		trunc = true
	}
	// O_APPEND is emulated on Plan 9
	if flag&O_APPEND == O_APPEND {
		flag = flag &^ O_APPEND
		append = true
	}

	syscall.ForkLock.RLock()
	if (create && trunc) || excl {
		fd, e = syscall.Create(name, flag, syscallMode(perm))
	} else {
		fd, e = syscall.Open(name, flag)
		if e != nil && create {
			var e1 error
			fd, e1 = syscall.Create(name, flag, syscallMode(perm))
			if e1 == nil {
				e = nil
			}
		}
	}
	syscall.ForkLock.RUnlock()

	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	if append {
		if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil {
			return nil, &PathError{"seek", name, e}
		}
	}

	return NewFile(uintptr(fd), name), nil
}
Example #5
0
// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to 0.
func (f *File) pread(b []byte, off int64) (n int, err int) {
	f.l.Lock()
	defer f.l.Unlock()
	curoffset, e := syscall.Seek(f.fd, 0, 1)
	if e != 0 {
		return 0, e
	}
	defer syscall.Seek(f.fd, curoffset, 0)
	o := syscall.Overlapped{
		OffsetHigh: uint32(off >> 32),
		Offset:     uint32(off),
	}
	var done uint32
	e = syscall.ReadFile(int32(f.fd), b, &done, &o)
	if e != 0 {
		return 0, e
	}
	return int(done), 0
}
Example #6
0
// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	curoffset, e := syscall.Seek(f.fd, 0, 1)
	if e != nil {
		return 0, e
	}
	defer syscall.Seek(f.fd, curoffset, 0)
	o := syscall.Overlapped{
		OffsetHigh: uint32(off >> 32),
		Offset:     uint32(off),
	}
	var done uint32
	e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
	if e != nil {
		return 0, e
	}
	return int(done), nil
}
Example #7
0
// Seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an Error, if any.
func (file *File) Seek(offset int64, whence int) (ret int64, err Error) {
	r, e := syscall.Seek(file.fd, offset, whence)
	if e == 0 && file.dirinfo != nil && r != 0 {
		e = syscall.EISDIR
	}
	if e != 0 {
		return 0, &PathError{"seek", file.name, Errno(e)}
	}
	return r, nil
}
Example #8
0
func (file *File) Seek(offset int64, whence int) (ret int, err os.Error) {
	if file == nil {
		return -1, os.EINVAL
	}
	fmt.Printf("file.Seek(%d,%d)\n", offset, whence)
	r, e := syscall.Seek(file.fd, offset, whence)
	if e != 0 {
		err = os.Errno(e)
	}
	return int(r), err
}
Example #9
0
func TestSeekFailure(t *testing.T) {
	_, err := syscall.Seek(-1, 0, 0)
	if err == nil {
		t.Fatalf("Seek(-1, 0, 0) did not fail")
	}
	str := err.Error() // used to crash on Linux
	t.Logf("Seek: %v", str)
	if str == "" {
		t.Fatalf("Seek(-1, 0, 0) return error with empty message")
	}
}
Example #10
0
// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// It returns the File and an error, if any.
func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
	var (
		fd     int
		e      syscall.Error
		create bool
		excl   bool
		trunc  bool
		append bool
	)

	if flag&O_CREATE == O_CREATE {
		flag = flag & ^O_CREATE
		create = true
	}
	if flag&O_EXCL == O_EXCL {
		excl = true
	}
	if flag&O_TRUNC == O_TRUNC {
		trunc = true
	}
	// O_APPEND is emulated on Plan 9
	if flag&O_APPEND == O_APPEND {
		flag = flag &^ O_APPEND
		append = true
	}

	syscall.ForkLock.RLock()
	if (create && trunc) || excl {
		fd, e = syscall.Create(name, flag, perm)
	} else {
		fd, e = syscall.Open(name, flag)
		if e != nil && create {
			var e1 syscall.Error
			fd, e1 = syscall.Create(name, flag, perm)
			if e1 == nil {
				e = nil
			}
		}
	}
	syscall.ForkLock.RUnlock()

	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	if append {
		if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil {
			return nil, &PathError{"seek", name, e}
		}
	}

	return NewFile(fd, name), nil
}
Example #11
0
func (dio *DiskIO) WriteBlock(blockaddr uint64, buffer []byte) (uint, error) {
	var noff int64 = int64(blockaddr) * int64(dio.BlockSize)
	_, err := syscall.Seek(dio.fileDescriptor, noff, 0)
	if err != nil {
		return 0, err
	}
	num, err := syscall.Write(dio.fileDescriptor, buffer)
	if err != nil {
		return 0, err
	}
	return uint(num), nil
}
Example #12
0
// OpenFile is the generalized open call; most users will use Open
// or Create instead. It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
	var (
		fd     int
		e      error
		create bool
		excl   bool
		trunc  bool
		append bool
	)

	if flag&O_CREATE == O_CREATE {
		flag = flag & ^O_CREATE
		create = true
	}
	if flag&O_EXCL == O_EXCL {
		excl = true
	}
	if flag&O_TRUNC == O_TRUNC {
		trunc = true
	}
	// O_APPEND is emulated on Plan 9
	if flag&O_APPEND == O_APPEND {
		flag = flag &^ O_APPEND
		append = true
	}

	if (create && trunc) || excl {
		fd, e = syscall.Create(name, flag, syscallMode(perm))
	} else {
		fd, e = syscall.Open(name, flag)
		if e != nil && create {
			var e1 error
			fd, e1 = syscall.Create(name, flag, syscallMode(perm))
			if e1 == nil {
				e = nil
			}
		}
	}

	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	if append {
		if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil {
			return nil, &PathError{"seek", name, e}
		}
	}

	return NewFile(uintptr(fd), name), nil
}
Example #13
0
// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to 0.
func (f *File) pread(b []byte, off int64) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	curoffset, e := syscall.Seek(f.fd, 0, 1)
	if e != nil {
		return 0, e
	}
	defer syscall.Seek(f.fd, curoffset, 0)
	o := syscall.Overlapped{
		OffsetHigh: uint32(off >> 32),
		Offset:     uint32(off),
	}
	var done uint32
	e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
	if e != nil {
		if e == syscall.ERROR_HANDLE_EOF {
			// end of file
			return 0, nil
		}
		return 0, e
	}
	return int(done), nil
}
Example #14
0
func (_entity *Entity) HashData(_hasher1 hasher.Hasher) (_hash []byte, _error Error) {
	if !_entity.IsRegular() {
		return nil, newError("dup", _entity.Path, -1, syscall.EBADF)
	}
	var _descriptor, _errno int
	_descriptor = _entity.Descriptor
	if _descriptor < 0 {
		return nil, newError("dup", _entity.Path, _entity.Descriptor, syscall.EBADF)
	}
	_descriptor, _errno = syscall.Dup(_descriptor)
	if _errno != 0 {
		return nil, newError("dup", _entity.Path, _entity.Descriptor, _errno)
	}
	defer syscall.Close(_descriptor)
	_, _errno = syscall.Seek(_descriptor, 0, 0 /* syscall.SEEK_SET */)
	if _errno != 0 {
		return nil, newError("seek", _entity.Path, _descriptor, _errno)
	}
	_hasher := _hasher1.Create()
	_buffer := make([]byte, _bufferSize)
	for {
		var _limit int
		_limit, _errno = syscall.Read(_descriptor, _buffer)
		if _errno != 0 {
			return nil, newError("read", _entity.Path, _descriptor, _errno)
		}
		if _limit == 0 {
			break
		}
		_error = hasher.HashUpdate(_hasher, _buffer[:_limit])
		if _error != nil {
			return nil, newError("hash", _entity.Path, _descriptor, syscall.EIO)
		}
	}
	_hash, _error = hasher.HashFinalize(_hasher)
	if _error != nil {
		return nil, newError("hash", _entity.Path, _descriptor, syscall.EIO)
	}
	if _entity.Hashes == nil {
		_entity.Hashes = make(map[string][]byte, 1)
	}
	_entity.Hashes[_hasher1.Identifier()] = _hash
	return _hash, nil
}
Example #15
0
func (g *GPIO) Wait() (bool, error) {
	if g.fd == 0 {
		fd, err := syscall.Open(g.valuePath, syscall.O_RDONLY, 0666)
		if err != nil {
			return false, err
		}
		g.fd = fd
		g.fdSet = new(syscall.FdSet)
		FD_SET(g.fd, g.fdSet)
		g.buf = make([]byte, 64)
		syscall.Read(g.fd, g.buf)
	}
	syscall.Select(g.fd+1, nil, nil, g.fdSet, nil)
	syscall.Seek(g.fd, 0, 0)
	_, err := syscall.Read(g.fd, g.buf)
	if err != nil {
		return false, err
	}
	return string(g.buf[:2]) == "1\n", nil
}
Example #16
0
func preadn(fd uintptr, buf []byte, off int) error {
	whence := 0
	if off < 0 {
		whence = 2
	}
	if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil {
		return err
	}
	for len(buf) > 0 {
		m, err := syscall.Read(int(fd), buf)
		if m <= 0 {
			if err == nil {
				return errors.New("short read")
			}
			return err
		}
		buf = buf[m:]
	}
	return nil
}
Example #17
0
func readValues(indices []int, filename string) []int32 {
	results := make([]int32, len(indices))
	b := make([]byte, 4)
	f, _ := os.Open(filename)
	fd := syscall.Handle(f.Fd())
	//value := int32(0)
	for i, cellIndex := range indices {
		syscall.Seek(fd, int64(cellIndex*4), os.SEEK_SET) // f.Seek(int64(cellIndex*4), os.SEEK_SET)
		syscall.Read(fd, b)                               //f.Read(b)
		value := bytes2int(b)                             // around 10-20% faster then binary.Read
		//package "encoding/binary"
		//binary.Read(f,binary.LittleEndian, &value)
		if value != -2147483648 {
			results[i] = value
		} else {
			results[i] = 99999
		}
	}
	return results
}
Example #18
0
// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	return syscall.Seek(f.fd, offset, whence)
}
Example #19
0
// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	return syscall.Seek(f.fd, offset, whence)
}
Example #20
0
func (watcher *PinWatcher) pinHandler() {
	exf, exf_err := os.OpenFile("/sys/class/gpio/export", syscall.O_WRONLY, 0400)
	if exf_err != nil {
		log.Panicln("Error opening /sys/class/gpio/export:", exf_err)
	}
	_, ex_err := exf.WriteString(fmt.Sprintf("%d", watcher.pin))
	if ex_err != nil {
		log.Panicln("Error writing to /sys/class/gpio/export:", ex_err)
	}
	exf.Close()
	time.Sleep(time.Microsecond)

	edge_file := fmt.Sprintf("/sys/class/gpio/gpio%d/edge", watcher.pin)
	edgef, edgef_err := os.OpenFile(edge_file, syscall.O_WRONLY, 0400)
	if edgef_err != nil {
		log.Panicf("Error opening %s: %s\n", edge_file, edgef_err)
	}
	_, edge_err := edgef.WriteString("both")
	if edge_err != nil {
		log.Panicf("Error writing to %s: %s\n", edge_file, edge_err)
	}
	edgef.Close()
	time.Sleep(time.Microsecond)

	value_file := fmt.Sprintf("/sys/class/gpio/gpio%d/value", watcher.pin)
	irq_fd, irq_err := syscall.Open(value_file, syscall.O_RDONLY|syscall.O_NONBLOCK, syscall.S_IREAD)
	if irq_err != nil {
		log.Panicln("Error opening %s: %s\n", value_file, irq_err)
	}

	epfd, eperr := syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
	if eperr != nil {
		log.Panicln("Error creating epoll:", eperr)
	}

	event := new(syscall.EpollEvent)
	event.Fd = int32(irq_fd)
	event.Events |= syscall.EPOLLPRI
	if ctlerr := syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, irq_fd, event); ctlerr != nil {
		log.Panicln("Error on epoll control operation:", ctlerr)
	}

	var events_buffer [10]syscall.EpollEvent
	var buf [1]byte
	for !watcher.shutdown {
		n, werr := syscall.EpollWait(epfd, events_buffer[0:], 4000)
		if werr != nil {
			log.Println("Epoll error:", werr)
		} else if n == 1 {
			syscall.Seek(irq_fd, 0, 0)
			syscall.Read(irq_fd, buf[0:])
			log.Println("Interrupt!", events_buffer[0].Fd, events_buffer[0].Events, buf)
		} else {
			log.Println("Timeout")
		}
	}

	syscall.Close(irq_fd)

	unexf, unexf_err := os.OpenFile("/sys/class/gpio/unexport", syscall.O_WRONLY|syscall.O_SYNC, 0400)
	if unexf_err != nil {
		log.Panicln("Error opening /sys/class/gpio/unexport:", unexf_err)
	}
	_, unex_err := unexf.WriteString(fmt.Sprintf("%d", watcher.pin))
	if unex_err != nil {
		log.Panicln("Error writing to /sys/class/gpio/unexport:", unex_err)
	}

	unexf.Close()

	watcher.wg.Done()
}
Example #21
0
func lseek(u U, a []uint64) uint64 {
	fd, offset, whence := int(a[0]), int64(a[1]), int(a[2])
	off, _ := syscall.Seek(fd, offset, whence)
	return uint64(off)
}
Example #22
0
// RetrieveLayoutStream0 streams sparse file data/hole layout
// Deprecated; Based on file.seek; use RetrieveLayoutStream instead
// To abort: abortStream <- error
// Check status: err := <- errStream
// Usage: go RetrieveLayoutStream(...)
func RetrieveLayoutStream0(abortStream <-chan error, file *os.File, r Interval, layoutStream chan<- FileInterval, errStream chan<- error) {
	curr := r.Begin

	// Data or hole?
	offsetData, errData := file.Seek(curr, seekData)
	offsetHole, errHole := file.Seek(curr, seekHole)
	var interval FileInterval
	if errData != nil {
		// Hole only
		interval = FileInterval{SparseHole, Interval{curr, r.End}}
		if interval.Len() > 0 {
			layoutStream <- interval
		}
		close(layoutStream)
		errStream <- nil
		return
	} else if errHole != nil {
		// Data only
		interval = FileInterval{SparseData, Interval{curr, r.End}}
		if interval.Len() > 0 {
			layoutStream <- interval
		}
		close(layoutStream)
		errStream <- nil
		return
	}

	if offsetData < offsetHole {
		interval = FileInterval{SparseData, Interval{curr, offsetHole}}
		curr = offsetHole
	} else {
		interval = FileInterval{SparseHole, Interval{curr, offsetData}}
		curr = offsetData
	}
	if interval.Len() > 0 {
		layoutStream <- interval
	}

	for curr < r.End {
		// Check abort condition
		select {
		case err := <-abortStream:
			close(layoutStream)
			errStream <- err
			return
		default:
		}

		var whence int
		if SparseData == interval.Kind {
			whence = seekData
		} else {
			whence = seekHole
		}

		// Note: file.Seek masks syscall.ENXIO hence syscall is used instead
		next, errno := syscall.Seek(int(file.Fd()), curr, whence)
		if errno != nil {
			switch errno {
			case syscall.ENXIO:
				// no more intervals
				next = r.End // close the last interval
			default:
				// mimic standard "os"" package error handler
				close(layoutStream)
				errStream <- &os.PathError{Op: "seek", Path: file.Name(), Err: errno}
				return
			}
		}
		if SparseData == interval.Kind {
			// End of data, handle the last hole if any
			interval = FileInterval{SparseHole, Interval{curr, next}}
		} else {
			// End of hole, handle the last data if any
			interval = FileInterval{SparseData, Interval{curr, next}}
		}
		curr = next
		if interval.Len() > 0 {
			layoutStream <- interval
		}
	}
	close(layoutStream)
	errStream <- nil
	return
}
Example #23
0
// This test is inspired on xfstest, t_dir_offset2.c
func TestReaddirPlusSeek(t *testing.T) {
	tc := NewTestCase(t)
	defer tc.Cleanup()

	var names []string
	for i := 0; i < 20; i++ {
		names = append(names, fmt.Sprintf("abcd%d", i))
	}

	for _, n := range names {
		if err := os.MkdirAll(filepath.Join(tc.origSubdir, n), 0755); err != nil {
			t.Fatalf("Mkdir failed: %v", err)
		}
	}

	fd, err := syscall.Open(tc.mountSubdir, syscall.O_RDONLY, 0755)
	if err != nil {
		t.Fatalf("Open(%q): %v", tc.mountSubdir, err)
	}
	defer syscall.Close(int(fd))

	// store offsets
	type entryOff struct {
		ino uint64
		off int64
	}
	previous := map[int]entryOff{}
	var bufdata [1024]byte
	for {
		buf := bufdata[:]
		n, err := syscall.ReadDirent(fd, buf)
		if err != nil {
			t.Fatalf("ReadDirent: %v", err)
		}
		if n == 0 {
			break
		}

		buf = buf[:n]
		for _, d := range parseDirents(buf) {
			if d.name == "." || d.name == ".." {
				continue
			}
			i := len(previous)
			previous[i] = entryOff{d.ino, d.off}
		}
	}

	for i := len(previous) - 1; i >= 0; i-- {
		var off int64
		if i > 0 {
			off = previous[i-1].off
		}

		if _, err := syscall.Seek(fd, off, 0); err != nil {
			t.Fatalf("Seek %v", err)
		}

		buf := bufdata[:]
		n, err := syscall.ReadDirent(fd, buf)
		if err != nil {
			t.Fatalf("readdir after seek %d: %v", i, err)
		}
		if n == 0 {
			t.Fatalf("no dirent after seek to %d", i)
		}

		ds := parseDirents(buf[:n])
		if ds[0].ino != previous[i].ino {
			t.Errorf("got ino %d, want %d", ds[0].ino, previous[i].ino)
		}
	}

	// Delete has a forget as side-effect: make sure we get the lookup counts correct.
	for _, n := range names {
		full := filepath.Join(tc.mountSubdir, n)
		if err := syscall.Rmdir(full); err != nil {
			t.Fatalf("Rmdir(%q): %v", n, err)
		}
	}
}