summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fsimpl/devpts
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fsimpl/devpts')
-rw-r--r--pkg/sentry/fsimpl/devpts/BUILD65
-rw-r--r--pkg/sentry/fsimpl/devpts/devpts_state_autogen.go502
-rw-r--r--pkg/sentry/fsimpl/devpts/devpts_test.go90
-rw-r--r--pkg/sentry/fsimpl/devpts/root_inode_refs.go140
4 files changed, 642 insertions, 155 deletions
diff --git a/pkg/sentry/fsimpl/devpts/BUILD b/pkg/sentry/fsimpl/devpts/BUILD
deleted file mode 100644
index f981ff296..000000000
--- a/pkg/sentry/fsimpl/devpts/BUILD
+++ /dev/null
@@ -1,65 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-load("//tools/go_generics:defs.bzl", "go_template_instance")
-
-licenses(["notice"])
-
-go_template_instance(
- name = "root_inode_refs",
- out = "root_inode_refs.go",
- package = "devpts",
- prefix = "rootInode",
- template = "//pkg/refsvfs2:refs_template",
- types = {
- "T": "rootInode",
- },
-)
-
-go_library(
- name = "devpts",
- srcs = [
- "devpts.go",
- "line_discipline.go",
- "master.go",
- "queue.go",
- "replica.go",
- "root_inode_refs.go",
- "terminal.go",
- ],
- visibility = ["//pkg/sentry:internal"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/context",
- "//pkg/errors/linuxerr",
- "//pkg/log",
- "//pkg/marshal",
- "//pkg/marshal/primitive",
- "//pkg/refs",
- "//pkg/refsvfs2",
- "//pkg/safemem",
- "//pkg/sentry/arch",
- "//pkg/sentry/fs",
- "//pkg/sentry/fs/lock",
- "//pkg/sentry/fsimpl/kernfs",
- "//pkg/sentry/kernel",
- "//pkg/sentry/kernel/auth",
- "//pkg/sentry/unimpl",
- "//pkg/sentry/vfs",
- "//pkg/sync",
- "//pkg/syserror",
- "//pkg/usermem",
- "//pkg/waiter",
- ],
-)
-
-go_test(
- name = "devpts_test",
- size = "small",
- srcs = ["devpts_test.go"],
- library = ":devpts",
- deps = [
- "//pkg/abi/linux",
- "//pkg/sentry/contexttest",
- "//pkg/usermem",
- "//pkg/waiter",
- ],
-)
diff --git a/pkg/sentry/fsimpl/devpts/devpts_state_autogen.go b/pkg/sentry/fsimpl/devpts/devpts_state_autogen.go
new file mode 100644
index 000000000..78e685c7e
--- /dev/null
+++ b/pkg/sentry/fsimpl/devpts/devpts_state_autogen.go
@@ -0,0 +1,502 @@
+// automatically generated by stateify.
+
+package devpts
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (fstype *FilesystemType) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.FilesystemType"
+}
+
+func (fstype *FilesystemType) StateFields() []string {
+ return []string{
+ "initErr",
+ "fs",
+ "root",
+ }
+}
+
+func (fstype *FilesystemType) beforeSave() {}
+
+// +checklocksignore
+func (fstype *FilesystemType) StateSave(stateSinkObject state.Sink) {
+ fstype.beforeSave()
+ stateSinkObject.Save(0, &fstype.initErr)
+ stateSinkObject.Save(1, &fstype.fs)
+ stateSinkObject.Save(2, &fstype.root)
+}
+
+func (fstype *FilesystemType) afterLoad() {}
+
+// +checklocksignore
+func (fstype *FilesystemType) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &fstype.initErr)
+ stateSourceObject.Load(1, &fstype.fs)
+ stateSourceObject.Load(2, &fstype.root)
+}
+
+func (fs *filesystem) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.filesystem"
+}
+
+func (fs *filesystem) StateFields() []string {
+ return []string{
+ "Filesystem",
+ "devMinor",
+ }
+}
+
+func (fs *filesystem) beforeSave() {}
+
+// +checklocksignore
+func (fs *filesystem) StateSave(stateSinkObject state.Sink) {
+ fs.beforeSave()
+ stateSinkObject.Save(0, &fs.Filesystem)
+ stateSinkObject.Save(1, &fs.devMinor)
+}
+
+func (fs *filesystem) afterLoad() {}
+
+// +checklocksignore
+func (fs *filesystem) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &fs.Filesystem)
+ stateSourceObject.Load(1, &fs.devMinor)
+}
+
+func (i *rootInode) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.rootInode"
+}
+
+func (i *rootInode) StateFields() []string {
+ return []string{
+ "implStatFS",
+ "InodeAlwaysValid",
+ "InodeAttrs",
+ "InodeDirectoryNoNewChildren",
+ "InodeNotSymlink",
+ "InodeTemporary",
+ "OrderedChildren",
+ "rootInodeRefs",
+ "locks",
+ "master",
+ "replicas",
+ "nextIdx",
+ }
+}
+
+func (i *rootInode) beforeSave() {}
+
+// +checklocksignore
+func (i *rootInode) StateSave(stateSinkObject state.Sink) {
+ i.beforeSave()
+ stateSinkObject.Save(0, &i.implStatFS)
+ stateSinkObject.Save(1, &i.InodeAlwaysValid)
+ stateSinkObject.Save(2, &i.InodeAttrs)
+ stateSinkObject.Save(3, &i.InodeDirectoryNoNewChildren)
+ stateSinkObject.Save(4, &i.InodeNotSymlink)
+ stateSinkObject.Save(5, &i.InodeTemporary)
+ stateSinkObject.Save(6, &i.OrderedChildren)
+ stateSinkObject.Save(7, &i.rootInodeRefs)
+ stateSinkObject.Save(8, &i.locks)
+ stateSinkObject.Save(9, &i.master)
+ stateSinkObject.Save(10, &i.replicas)
+ stateSinkObject.Save(11, &i.nextIdx)
+}
+
+func (i *rootInode) afterLoad() {}
+
+// +checklocksignore
+func (i *rootInode) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &i.implStatFS)
+ stateSourceObject.Load(1, &i.InodeAlwaysValid)
+ stateSourceObject.Load(2, &i.InodeAttrs)
+ stateSourceObject.Load(3, &i.InodeDirectoryNoNewChildren)
+ stateSourceObject.Load(4, &i.InodeNotSymlink)
+ stateSourceObject.Load(5, &i.InodeTemporary)
+ stateSourceObject.Load(6, &i.OrderedChildren)
+ stateSourceObject.Load(7, &i.rootInodeRefs)
+ stateSourceObject.Load(8, &i.locks)
+ stateSourceObject.Load(9, &i.master)
+ stateSourceObject.Load(10, &i.replicas)
+ stateSourceObject.Load(11, &i.nextIdx)
+}
+
+func (i *implStatFS) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.implStatFS"
+}
+
+func (i *implStatFS) StateFields() []string {
+ return []string{}
+}
+
+func (i *implStatFS) beforeSave() {}
+
+// +checklocksignore
+func (i *implStatFS) StateSave(stateSinkObject state.Sink) {
+ i.beforeSave()
+}
+
+func (i *implStatFS) afterLoad() {}
+
+// +checklocksignore
+func (i *implStatFS) StateLoad(stateSourceObject state.Source) {
+}
+
+func (l *lineDiscipline) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.lineDiscipline"
+}
+
+func (l *lineDiscipline) StateFields() []string {
+ return []string{
+ "size",
+ "inQueue",
+ "outQueue",
+ "termios",
+ "column",
+ "masterWaiter",
+ "replicaWaiter",
+ }
+}
+
+func (l *lineDiscipline) beforeSave() {}
+
+// +checklocksignore
+func (l *lineDiscipline) StateSave(stateSinkObject state.Sink) {
+ l.beforeSave()
+ stateSinkObject.Save(0, &l.size)
+ stateSinkObject.Save(1, &l.inQueue)
+ stateSinkObject.Save(2, &l.outQueue)
+ stateSinkObject.Save(3, &l.termios)
+ stateSinkObject.Save(4, &l.column)
+ stateSinkObject.Save(5, &l.masterWaiter)
+ stateSinkObject.Save(6, &l.replicaWaiter)
+}
+
+func (l *lineDiscipline) afterLoad() {}
+
+// +checklocksignore
+func (l *lineDiscipline) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &l.size)
+ stateSourceObject.Load(1, &l.inQueue)
+ stateSourceObject.Load(2, &l.outQueue)
+ stateSourceObject.Load(3, &l.termios)
+ stateSourceObject.Load(4, &l.column)
+ stateSourceObject.Load(5, &l.masterWaiter)
+ stateSourceObject.Load(6, &l.replicaWaiter)
+}
+
+func (o *outputQueueTransformer) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.outputQueueTransformer"
+}
+
+func (o *outputQueueTransformer) StateFields() []string {
+ return []string{}
+}
+
+func (o *outputQueueTransformer) beforeSave() {}
+
+// +checklocksignore
+func (o *outputQueueTransformer) StateSave(stateSinkObject state.Sink) {
+ o.beforeSave()
+}
+
+func (o *outputQueueTransformer) afterLoad() {}
+
+// +checklocksignore
+func (o *outputQueueTransformer) StateLoad(stateSourceObject state.Source) {
+}
+
+func (i *inputQueueTransformer) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.inputQueueTransformer"
+}
+
+func (i *inputQueueTransformer) StateFields() []string {
+ return []string{}
+}
+
+func (i *inputQueueTransformer) beforeSave() {}
+
+// +checklocksignore
+func (i *inputQueueTransformer) StateSave(stateSinkObject state.Sink) {
+ i.beforeSave()
+}
+
+func (i *inputQueueTransformer) afterLoad() {}
+
+// +checklocksignore
+func (i *inputQueueTransformer) StateLoad(stateSourceObject state.Source) {
+}
+
+func (mi *masterInode) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.masterInode"
+}
+
+func (mi *masterInode) StateFields() []string {
+ return []string{
+ "implStatFS",
+ "InodeAttrs",
+ "InodeNoopRefCount",
+ "InodeNotDirectory",
+ "InodeNotSymlink",
+ "locks",
+ "root",
+ }
+}
+
+func (mi *masterInode) beforeSave() {}
+
+// +checklocksignore
+func (mi *masterInode) StateSave(stateSinkObject state.Sink) {
+ mi.beforeSave()
+ stateSinkObject.Save(0, &mi.implStatFS)
+ stateSinkObject.Save(1, &mi.InodeAttrs)
+ stateSinkObject.Save(2, &mi.InodeNoopRefCount)
+ stateSinkObject.Save(3, &mi.InodeNotDirectory)
+ stateSinkObject.Save(4, &mi.InodeNotSymlink)
+ stateSinkObject.Save(5, &mi.locks)
+ stateSinkObject.Save(6, &mi.root)
+}
+
+func (mi *masterInode) afterLoad() {}
+
+// +checklocksignore
+func (mi *masterInode) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &mi.implStatFS)
+ stateSourceObject.Load(1, &mi.InodeAttrs)
+ stateSourceObject.Load(2, &mi.InodeNoopRefCount)
+ stateSourceObject.Load(3, &mi.InodeNotDirectory)
+ stateSourceObject.Load(4, &mi.InodeNotSymlink)
+ stateSourceObject.Load(5, &mi.locks)
+ stateSourceObject.Load(6, &mi.root)
+}
+
+func (mfd *masterFileDescription) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.masterFileDescription"
+}
+
+func (mfd *masterFileDescription) StateFields() []string {
+ return []string{
+ "vfsfd",
+ "FileDescriptionDefaultImpl",
+ "LockFD",
+ "inode",
+ "t",
+ }
+}
+
+func (mfd *masterFileDescription) beforeSave() {}
+
+// +checklocksignore
+func (mfd *masterFileDescription) StateSave(stateSinkObject state.Sink) {
+ mfd.beforeSave()
+ stateSinkObject.Save(0, &mfd.vfsfd)
+ stateSinkObject.Save(1, &mfd.FileDescriptionDefaultImpl)
+ stateSinkObject.Save(2, &mfd.LockFD)
+ stateSinkObject.Save(3, &mfd.inode)
+ stateSinkObject.Save(4, &mfd.t)
+}
+
+func (mfd *masterFileDescription) afterLoad() {}
+
+// +checklocksignore
+func (mfd *masterFileDescription) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &mfd.vfsfd)
+ stateSourceObject.Load(1, &mfd.FileDescriptionDefaultImpl)
+ stateSourceObject.Load(2, &mfd.LockFD)
+ stateSourceObject.Load(3, &mfd.inode)
+ stateSourceObject.Load(4, &mfd.t)
+}
+
+func (q *queue) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.queue"
+}
+
+func (q *queue) StateFields() []string {
+ return []string{
+ "readBuf",
+ "waitBuf",
+ "waitBufLen",
+ "readable",
+ "transformer",
+ }
+}
+
+func (q *queue) beforeSave() {}
+
+// +checklocksignore
+func (q *queue) StateSave(stateSinkObject state.Sink) {
+ q.beforeSave()
+ stateSinkObject.Save(0, &q.readBuf)
+ stateSinkObject.Save(1, &q.waitBuf)
+ stateSinkObject.Save(2, &q.waitBufLen)
+ stateSinkObject.Save(3, &q.readable)
+ stateSinkObject.Save(4, &q.transformer)
+}
+
+func (q *queue) afterLoad() {}
+
+// +checklocksignore
+func (q *queue) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &q.readBuf)
+ stateSourceObject.Load(1, &q.waitBuf)
+ stateSourceObject.Load(2, &q.waitBufLen)
+ stateSourceObject.Load(3, &q.readable)
+ stateSourceObject.Load(4, &q.transformer)
+}
+
+func (ri *replicaInode) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.replicaInode"
+}
+
+func (ri *replicaInode) StateFields() []string {
+ return []string{
+ "implStatFS",
+ "InodeAttrs",
+ "InodeNoopRefCount",
+ "InodeNotDirectory",
+ "InodeNotSymlink",
+ "locks",
+ "root",
+ "t",
+ }
+}
+
+func (ri *replicaInode) beforeSave() {}
+
+// +checklocksignore
+func (ri *replicaInode) StateSave(stateSinkObject state.Sink) {
+ ri.beforeSave()
+ stateSinkObject.Save(0, &ri.implStatFS)
+ stateSinkObject.Save(1, &ri.InodeAttrs)
+ stateSinkObject.Save(2, &ri.InodeNoopRefCount)
+ stateSinkObject.Save(3, &ri.InodeNotDirectory)
+ stateSinkObject.Save(4, &ri.InodeNotSymlink)
+ stateSinkObject.Save(5, &ri.locks)
+ stateSinkObject.Save(6, &ri.root)
+ stateSinkObject.Save(7, &ri.t)
+}
+
+func (ri *replicaInode) afterLoad() {}
+
+// +checklocksignore
+func (ri *replicaInode) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &ri.implStatFS)
+ stateSourceObject.Load(1, &ri.InodeAttrs)
+ stateSourceObject.Load(2, &ri.InodeNoopRefCount)
+ stateSourceObject.Load(3, &ri.InodeNotDirectory)
+ stateSourceObject.Load(4, &ri.InodeNotSymlink)
+ stateSourceObject.Load(5, &ri.locks)
+ stateSourceObject.Load(6, &ri.root)
+ stateSourceObject.Load(7, &ri.t)
+}
+
+func (rfd *replicaFileDescription) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.replicaFileDescription"
+}
+
+func (rfd *replicaFileDescription) StateFields() []string {
+ return []string{
+ "vfsfd",
+ "FileDescriptionDefaultImpl",
+ "LockFD",
+ "inode",
+ }
+}
+
+func (rfd *replicaFileDescription) beforeSave() {}
+
+// +checklocksignore
+func (rfd *replicaFileDescription) StateSave(stateSinkObject state.Sink) {
+ rfd.beforeSave()
+ stateSinkObject.Save(0, &rfd.vfsfd)
+ stateSinkObject.Save(1, &rfd.FileDescriptionDefaultImpl)
+ stateSinkObject.Save(2, &rfd.LockFD)
+ stateSinkObject.Save(3, &rfd.inode)
+}
+
+func (rfd *replicaFileDescription) afterLoad() {}
+
+// +checklocksignore
+func (rfd *replicaFileDescription) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &rfd.vfsfd)
+ stateSourceObject.Load(1, &rfd.FileDescriptionDefaultImpl)
+ stateSourceObject.Load(2, &rfd.LockFD)
+ stateSourceObject.Load(3, &rfd.inode)
+}
+
+func (r *rootInodeRefs) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.rootInodeRefs"
+}
+
+func (r *rootInodeRefs) StateFields() []string {
+ return []string{
+ "refCount",
+ }
+}
+
+func (r *rootInodeRefs) beforeSave() {}
+
+// +checklocksignore
+func (r *rootInodeRefs) StateSave(stateSinkObject state.Sink) {
+ r.beforeSave()
+ stateSinkObject.Save(0, &r.refCount)
+}
+
+// +checklocksignore
+func (r *rootInodeRefs) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &r.refCount)
+ stateSourceObject.AfterLoad(r.afterLoad)
+}
+
+func (tm *Terminal) StateTypeName() string {
+ return "pkg/sentry/fsimpl/devpts.Terminal"
+}
+
+func (tm *Terminal) StateFields() []string {
+ return []string{
+ "n",
+ "ld",
+ "masterKTTY",
+ "replicaKTTY",
+ }
+}
+
+func (tm *Terminal) beforeSave() {}
+
+// +checklocksignore
+func (tm *Terminal) StateSave(stateSinkObject state.Sink) {
+ tm.beforeSave()
+ stateSinkObject.Save(0, &tm.n)
+ stateSinkObject.Save(1, &tm.ld)
+ stateSinkObject.Save(2, &tm.masterKTTY)
+ stateSinkObject.Save(3, &tm.replicaKTTY)
+}
+
+func (tm *Terminal) afterLoad() {}
+
+// +checklocksignore
+func (tm *Terminal) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &tm.n)
+ stateSourceObject.Load(1, &tm.ld)
+ stateSourceObject.Load(2, &tm.masterKTTY)
+ stateSourceObject.Load(3, &tm.replicaKTTY)
+}
+
+func init() {
+ state.Register((*FilesystemType)(nil))
+ state.Register((*filesystem)(nil))
+ state.Register((*rootInode)(nil))
+ state.Register((*implStatFS)(nil))
+ state.Register((*lineDiscipline)(nil))
+ state.Register((*outputQueueTransformer)(nil))
+ state.Register((*inputQueueTransformer)(nil))
+ state.Register((*masterInode)(nil))
+ state.Register((*masterFileDescription)(nil))
+ state.Register((*queue)(nil))
+ state.Register((*replicaInode)(nil))
+ state.Register((*replicaFileDescription)(nil))
+ state.Register((*rootInodeRefs)(nil))
+ state.Register((*Terminal)(nil))
+}
diff --git a/pkg/sentry/fsimpl/devpts/devpts_test.go b/pkg/sentry/fsimpl/devpts/devpts_test.go
deleted file mode 100644
index 1ef07d702..000000000
--- a/pkg/sentry/fsimpl/devpts/devpts_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package devpts
-
-import (
- "testing"
-
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/usermem"
- "gvisor.dev/gvisor/pkg/waiter"
-)
-
-func TestSimpleMasterToReplica(t *testing.T) {
- ld := newLineDiscipline(linux.DefaultReplicaTermios)
- ctx := contexttest.Context(t)
- inBytes := []byte("hello, tty\n")
- src := usermem.BytesIOSequence(inBytes)
- outBytes := make([]byte, 32)
- dst := usermem.BytesIOSequence(outBytes)
-
- // Write to the input queue.
- nw, err := ld.inputQueueWrite(ctx, src)
- if err != nil {
- t.Fatalf("error writing to input queue: %v", err)
- }
- if nw != int64(len(inBytes)) {
- t.Fatalf("wrote wrong length: got %d, want %d", nw, len(inBytes))
- }
-
- // Read from the input queue.
- nr, err := ld.inputQueueRead(ctx, dst)
- if err != nil {
- t.Fatalf("error reading from input queue: %v", err)
- }
- if nr != int64(len(inBytes)) {
- t.Fatalf("read wrong length: got %d, want %d", nr, len(inBytes))
- }
-
- outStr := string(outBytes[:nr])
- inStr := string(inBytes)
- if outStr != inStr {
- t.Fatalf("written and read strings do not match: got %q, want %q", outStr, inStr)
- }
-}
-
-type callback func(*waiter.Entry, waiter.EventMask)
-
-func (cb callback) Callback(entry *waiter.Entry, mask waiter.EventMask) {
- cb(entry, mask)
-}
-
-func TestEchoDeadlock(t *testing.T) {
- ctx := contexttest.Context(t)
- termios := linux.DefaultReplicaTermios
- termios.LocalFlags |= linux.ECHO
- ld := newLineDiscipline(termios)
- outBytes := make([]byte, 32)
- dst := usermem.BytesIOSequence(outBytes)
- entry := &waiter.Entry{Callback: callback(func(*waiter.Entry, waiter.EventMask) {
- ld.inputQueueRead(ctx, dst)
- })}
- ld.masterWaiter.EventRegister(entry, waiter.ReadableEvents)
- defer ld.masterWaiter.EventUnregister(entry)
- inBytes := []byte("hello, tty\n")
- n, err := ld.inputQueueWrite(ctx, usermem.BytesIOSequence(inBytes))
- if err != nil {
- t.Fatalf("inputQueueWrite: %v", err)
- }
- if int(n) != len(inBytes) {
- t.Fatalf("read wrong length: got %d, want %d", n, len(inBytes))
- }
- outStr := string(outBytes[:n])
- inStr := string(inBytes)
- if outStr != inStr {
- t.Fatalf("written and read strings do not match: got %q, want %q", outStr, inStr)
- }
-}
diff --git a/pkg/sentry/fsimpl/devpts/root_inode_refs.go b/pkg/sentry/fsimpl/devpts/root_inode_refs.go
new file mode 100644
index 000000000..e53739a90
--- /dev/null
+++ b/pkg/sentry/fsimpl/devpts/root_inode_refs.go
@@ -0,0 +1,140 @@
+package devpts
+
+import (
+ "fmt"
+ "sync/atomic"
+
+ "gvisor.dev/gvisor/pkg/refsvfs2"
+)
+
+// enableLogging indicates whether reference-related events should be logged (with
+// stack traces). This is false by default and should only be set to true for
+// debugging purposes, as it can generate an extremely large amount of output
+// and drastically degrade performance.
+const rootInodeenableLogging = false
+
+// obj is used to customize logging. Note that we use a pointer to T so that
+// we do not copy the entire object when passed as a format parameter.
+var rootInodeobj *rootInode
+
+// Refs implements refs.RefCounter. It keeps a reference count using atomic
+// operations and calls the destructor when the count reaches zero.
+//
+// NOTE: Do not introduce additional fields to the Refs struct. It is used by
+// many filesystem objects, and we want to keep it as small as possible (i.e.,
+// the same size as using an int64 directly) to avoid taking up extra cache
+// space. In general, this template should not be extended at the cost of
+// performance. If it does not offer enough flexibility for a particular object
+// (example: b/187877947), we should implement the RefCounter/CheckedObject
+// interfaces manually.
+//
+// +stateify savable
+type rootInodeRefs struct {
+ // refCount is composed of two fields:
+ //
+ // [32-bit speculative references]:[32-bit real references]
+ //
+ // Speculative references are used for TryIncRef, to avoid a CompareAndSwap
+ // loop. See IncRef, DecRef and TryIncRef for details of how these fields are
+ // used.
+ refCount int64
+}
+
+// InitRefs initializes r with one reference and, if enabled, activates leak
+// checking.
+func (r *rootInodeRefs) InitRefs() {
+ atomic.StoreInt64(&r.refCount, 1)
+ refsvfs2.Register(r)
+}
+
+// RefType implements refsvfs2.CheckedObject.RefType.
+func (r *rootInodeRefs) RefType() string {
+ return fmt.Sprintf("%T", rootInodeobj)[1:]
+}
+
+// LeakMessage implements refsvfs2.CheckedObject.LeakMessage.
+func (r *rootInodeRefs) LeakMessage() string {
+ return fmt.Sprintf("[%s %p] reference count of %d instead of 0", r.RefType(), r, r.ReadRefs())
+}
+
+// LogRefs implements refsvfs2.CheckedObject.LogRefs.
+func (r *rootInodeRefs) LogRefs() bool {
+ return rootInodeenableLogging
+}
+
+// ReadRefs returns the current number of references. The returned count is
+// inherently racy and is unsafe to use without external synchronization.
+func (r *rootInodeRefs) ReadRefs() int64 {
+ return atomic.LoadInt64(&r.refCount)
+}
+
+// IncRef implements refs.RefCounter.IncRef.
+//
+//go:nosplit
+func (r *rootInodeRefs) IncRef() {
+ v := atomic.AddInt64(&r.refCount, 1)
+ if rootInodeenableLogging {
+ refsvfs2.LogIncRef(r, v)
+ }
+ if v <= 1 {
+ panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType()))
+ }
+}
+
+// TryIncRef implements refs.TryRefCounter.TryIncRef.
+//
+// To do this safely without a loop, a speculative reference is first acquired
+// on the object. This allows multiple concurrent TryIncRef calls to distinguish
+// other TryIncRef calls from genuine references held.
+//
+//go:nosplit
+func (r *rootInodeRefs) TryIncRef() bool {
+ const speculativeRef = 1 << 32
+ if v := atomic.AddInt64(&r.refCount, speculativeRef); int32(v) == 0 {
+
+ atomic.AddInt64(&r.refCount, -speculativeRef)
+ return false
+ }
+
+ v := atomic.AddInt64(&r.refCount, -speculativeRef+1)
+ if rootInodeenableLogging {
+ refsvfs2.LogTryIncRef(r, v)
+ }
+ return true
+}
+
+// DecRef implements refs.RefCounter.DecRef.
+//
+// Note that speculative references are counted here. Since they were added
+// prior to real references reaching zero, they will successfully convert to
+// real references. In other words, we see speculative references only in the
+// following case:
+//
+// A: TryIncRef [speculative increase => sees non-negative references]
+// B: DecRef [real decrease]
+// A: TryIncRef [transform speculative to real]
+//
+//go:nosplit
+func (r *rootInodeRefs) DecRef(destroy func()) {
+ v := atomic.AddInt64(&r.refCount, -1)
+ if rootInodeenableLogging {
+ refsvfs2.LogDecRef(r, v)
+ }
+ switch {
+ case v < 0:
+ panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType()))
+
+ case v == 0:
+ refsvfs2.Unregister(r)
+
+ if destroy != nil {
+ destroy()
+ }
+ }
+}
+
+func (r *rootInodeRefs) afterLoad() {
+ if r.ReadRefs() > 0 {
+ refsvfs2.Register(r)
+ }
+}