ops

package
v0.29.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2023 License: Apache-2.0 Imports: 11 Imported by: 8

Documentation

Overview

Package ops provides a Golang-idiomatic API to the query and switching operations on Linux-kernel namespaces, hiding ioctl()s and syscalls.

Namespace Queries

A particular Linux-kernel namespace can be referenced by a filesystem path, an open file descriptor, or an *os.File. Thus, this package defines the following three (six) namespace reference types:

The only difference between the NamespaceXXX and TypedNamespaceXXX reference types are: if the the type of namespace referenced is known beforehand, then this knowledge might be used to either optimize relations.Type lookups, or support Linux kernels before 4.11 which lack the ability to query the type of namespace via an ioctl(). In particular, this allows using the Visit function (see below) to be used on such older kernels.

All these types of namespace references define the following query operations from the relations.Relation interface (which map to a set of ioctl() calls, see ioctl_ns(2), with the exception of the ID query):

NamespacePath and NamespaceFd can be easily converted from or to string and uintptr respectively.

netns := NamespacePath("/proc/self/ns/net")
path := string(netns)

In case you want to use the Visit function for switching namespaces and you need to support Linux kernels before 4.11 (which lack a required ioctl) then you can resort to TypedNamespacePath instead of NamespacePath.

netns := TypedNamespacePath("/proc/self/ns/net", species.CLONE_NEWNET)

As NamespaceFile mirrors os.File it cannot be directly converted in the way NamespacePath and NamespaceFd can. However, things are not overly complex either when keeping the following conversion examples in mind. To create a *NamespaceFile from an *os.File, such as returned by os.Open, simply use the NewNamespaceFile wrapper:

nsf, err := NewNamespaceFile(os.Open("/proc/self/ns/net"))

The rationale here is to model NamespaceFile as close as possible to os.File, and this implies that it should not be possible to create a NamespaceFile from a nil *os.File.

Please note that NewNamespaceFile expects two parameters, an *os.File as well as an [error]. Simply specify a nil error in code contexts where there is clear that the *os.File is valid and there was no error in getting it.

Getting back an *os.File in case it is explicitly required is also simple:

f := &nsf.File

There's no need to panic because NamespaceFile embeds os.File, as opposed to *os.File, on purpose: os.File is a struct which consists solely of a single *os.file pointer to an implementation-internal structure. By embedding the outer struct instead of a pointer to it we mimic the original handling as close as possible, avoiding situations where a non-nil NamespaceFile points to a nil *os.File.

Switching Namespaces

Switching namespaces is a slightly messy business in Golang: it is subject to both Golang runtime limitations as well as Linux kernel restrictions imposed especially on multi-threaded processes. In particular, after the Golang runtime has started, threads cannot change their user namespaces and mount namespaces anymore. Also, processes in general cannot switch themselves into a different PID namespace, but only their future child processes. Luckily, switching other types of namespaces is less restricted, such as switching a specific Go routine (rather, its locked OS thread) into another network namespace (and back again) is almost painless. However, OS threads need to hold both sufficient effective privileges for themselves as well as they must have sufficient (evaluated) capabilities in the namespace to switch to, please see setns(2) and user_namespaces(7) for details about the specific capabilities needed and how capabilities of a process with relation to a destination namespace are evaluated.

This package provides three means to execute some Go code in an OS thread with namespaces switched as specified:

  • Go(f, namespaces...) – asynchronous f in the specified namespaces.
  • Execute(f, namespaces...) – synchronous f in the specified namespaces with result.
  • Visit(f, namespaces...) – synchronous f in the specified namespaces in same Go routine.

These namespace-switching methods differ as follows: Go(f, namespaces...) acts very similar to the “go” statement in that it runs the given function f as a new go routine, but with its executing OS thread locked and switched into the specified namespaces.

Execute(f, namespaces...) is a synchronous version of Go() which waits for the namespace-switched f() to complete and to return some result (in form of an interface{}). Execute then returns this result to the caller.

Visit(f, namespaces...) is for those situations where the caller wants to avoid creating a new go routine, but is prepared to throw away its current go routine in case Visit fails switching out of the namespaces afterwards, so the current OS thread and its go routine is toast.

Go

The Go function runs a function as a Go routine in the specified namespace(s). It returns an error in case switching into the specified namespaces fails, otherwise it simply returns nil. Please note that Go doesn't call the specified function synchronously, but instead as a new Go routine.

netns := ops.NamespacePath("/proc/self/ns/net")
err := ops.Go(func() {
    fmt.Println("Nobody expects the Spanish Inquisition!")
}, netns)

While this might seem inconvenient at first, this design actually is very robust in view of any problems that might pop up when trying to switch the current (locaked) OS thread back into its original namespaces; the OS thread would be unrecoverable, but without a way to disassociate its Go routine from it. The Go() function avoids this situation by executing the desired function in a throw-away Go routine, so the Golang runtime can easily throw away the tainted OS thread which is locked to it afterwards. Sometimes, throwing things away is much cleaner (and not only for certain types of PPE).

If a Golang process needs to switch mount, PID, and user namespaces, we recommend using the gons package in combination with its reexec subpackage (gons provide namespace switching before the Golang runtime starts, while reexec forks a Golang process and reexecutes it, with the reexecuted child then runnining a specific function only in the specified namespaces).

Execute

Execute is the synchronous twin of Go(): it waits for the namespace-switched function f() to complete and to return an interface{}. Execute then passes on this result to its caller.

netns := ops.NamespacePath("/proc/self/ns/net")
result, err := ops.Execute(func() interface{} {
    return "Nobody expects the Spanish Inquisition!"
}, netns)

Visit

If unsure, use Go or Execute instead. Only use Visit if you understand that it can get you in really hot water and you are prepared to accept any consequences.

In case a go routine wants to hop into a namespace and then out of it again, without the help of a new go-routine, then Visit helps with that. However, due to Golang's runtime design, if getting back to the original namespaces before the call to Visit fails, then any such go routine must be prepare to sacrifice itself, because by then it has a locked OS thread on its back in an unknown namespace attachment state, and further namespace hopping might end badly.

If the caller is in a throw-away go routine itself and needs to run some code synchronously in other namespaces, then Visit gives some optimization over Go and especially Execute, as it avoids spinning up another go routine.

For more background information please see M0 is Special.

Namespace IDs

This package works with namespace identifiers in the form of tuples made from the inode number of a namespace and the associated filesystem device number (device ID). While at this time the inode number would be sufficient, as currently all namespaces are solely managed through the so-called “nsfs” filesystem, the tuple-based model follows the advice from namespaces(7) as well as the dire kernel developer warning to create more havoc by deploying multiple namespace filesystem instances.

Comparing namespaces for equality is as simple as, as long both identifiers come from an origin honoring the device IDs:

if ns1.ID() == ns2.ID() {
	// ...
}

No special Equal methods, nothing, nada, zilch: just plain "==".

Unfortunately, the same kernel devs ignored their own dire warnings and happily output any namespace textual reference using only the inode number, such as in “net:[4026531905]”. And they left it to us to face the music they're playing; CLI tools so far only use the kernel's incomplete textual format. To ease a future transition, species.IDwithType("net:[...]") returns complete namespace identifier information by supplying the missing device ID for the current nsfs filesystem itself.

In consequence, user code should avoid creating any species.NamespaceID directly, but instead through species.IDwithType, such as:

nsid1, _ := species.IDwithType("net:[4026531905]")

or, given the inode as a number, not text, using species.NamespaceIDFromInode:

nsid1 := species.NamespaceIDFromInode(4026531905)

Sigh.

Example (ID)
id, _ := NamespacePath("/proc/self/ns/net").ID()
fmt.Println("id of my network namespace:", id)
Output:

Example (OwnerUID)
uid, _ := NamespacePath("/proc/self/ns/user").OwnerUID()
fmt.Println("user namespace id owning my network namespace:", uid)
Output:

Example (Parent)
parentuserns, _ := NamespacePath("/proc/self/ns/user").Parent()
id, _ := parentuserns.ID()
// Release OS-level resources held by the returned parent user namespace
// reference.
_ = parentuserns.(io.Closer).Close()
fmt.Println("parent user namespace id of my user namespace:", id)
Output:

Example (Type)
nstype, _ := NamespacePath("/proc/self/ns/pid").Type()
fmt.Printf("0x%08x\n", uint(nstype))
fmt.Println(nstype.String())
Output:

0x20000000
CLONE_NEWPID
Example (User)
userns, _ := NamespacePath("/proc/self/ns/net").User()
id, _ := userns.ID()
// Release OS-level resources held by the returned user namespace reference.
_ = userns.(io.Closer).Close()
fmt.Println("user namespace id owning my network namespace:", id)
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Execute

func Execute(f func() interface{}, nsrefs ...relations.Relation) (interface{}, error)

Execute a function synchronously while switched into the specified namespaces, then returns the interface{} outcome of calling the specified function. If switching fails, Execute returns an error instead.

func Go

func Go(f func(), nsrefs ...relations.Relation) error

Go runs the specified function as a new Go routine and from a locked OS thread, while joined to the specified namespaces. When the specified function returns, its Go routine will also terminate and the underlying OS thread will be destroyed. This avoids subtle problems further down the road in case there were namespace switching issues which overwise would carry over into any code executed after invoking Go. Go returns nil if switching namespaces succeeded, else an error. Please note that Go returns as soon as switching namespaces has finished. The specified function is then run in its own Go routine.

func Visit

func Visit(f func(), nsrefs ...relations.Relation) (err error)

Visit locks the OS thread executing the current go routine, then switches the thread into the specified namespaces, and executes f. f must be a function taking no arguments and returning nothing. Afterwards, it switches the namespaces back to their original settings and unlocks the underlying OS thread.

If switching namespaces back fails, then the OS thread is tainted and will remain locked. As simple as this sounds, Visit can be a dangerous thing: as long as the wheels keep spinning we're in the sunlit uplands. But as soon as the gears jam ... good luck. Visit mainly exists for those optimizations where creating new OS threads is deemed to much overhead and namespace switch-back usually is possible. However, such uses must be prepared for Visit to fail and then act accordingly: namely, terminate the Go routine so the runtime can kill its locked OS thread.

Visit should never be called from the main Go routine, as any failure in switching namespaces leaves us with a tainted OS thread for the main Go routine and terminating the main Go routine isn't a good idea either. Yuk!

Types

type InvalidNamespaceError

type InvalidNamespaceError struct {
	Ref string // textual representation of a namespace reference.
	Err error  // wrapped OS-level error.
}

InvalidNamespaceError wraps an underlying OS-related error when dealing with Linux-kernel namespaces. Due to Golang's attempt at abstracting things, this might often be an os.PathError, in its turn wrapping a syscall error, such as syscall.EBADF, syscall.EINVAL, syscall.EPERM, et cetera.

func (*InvalidNamespaceError) Error

func (e *InvalidNamespaceError) Error() string

Error returns a textual description of this invalid namespace error.

func (*InvalidNamespaceError) Unwrap

func (e *InvalidNamespaceError) Unwrap() error

Unwrap returns the error underlying an invalid namespace error.

type NamespaceFd

type NamespaceFd int

NamespaceFd references a Linux-kernel namespace via an open file descriptor. Following Unix tradition for file descriptors, NamespaceFd is an alias for an int (and not an uintptr, as in some cross-platform parts of the Golang packages). Please note that a NamespaceFd reference aliases a file descriptor, but it does not take ownership of it.

func (NamespaceFd) ID

func (nsfd NamespaceFd) ID() (species.NamespaceID, error)

ID returns the namespace ID in form of its inode number of the Linux-kernel namespace referenced by this open file descriptor. Please be aware that ID even returns an inode number if the file descriptor doesn't reference a namespace but instead some other open file.

func (NamespaceFd) NsFd

func (nsfd NamespaceFd) NsFd() (fd int, closer opener.FdCloser, err error)

NsFd returns an open file descriptor which references the namespace. After the file descriptor is no longer needed, the caller must call the returned close function, in order to avoid wasting file descriptors.

Please note that in case of a NamespaceFd reference, this returns the original open file descriptor (and doesn't make a copy of it). Aliasing a file descriptor into a NamespaceFd does not take ownership, so control of the lifetime of the aliased file descriptor is still up to its original creator. In consequence, the closer returned for a namespace file descriptor will leave the original file descriptor untouched.

func (NamespaceFd) OpenTypedReference

func (nsfd NamespaceFd) OpenTypedReference() (relations.Relation, opener.ReferenceCloser, error)

OpenTypedReference returns an open namespace reference, from which an OS-level file descriptor can be retrieved using NamespaceFd.NsFd. OpenTypeReference is internally used to allow optimizing switching namespaces under the condition that additionally the type of namespace needs to be known at the same time.

func (NamespaceFd) OwnerUID

func (nsfd NamespaceFd) OwnerUID() (int, error)

OwnerUID returns the user id (UID) of the user namespace referenced by this open file descriptor.

🛈 A Linux kernel version 4.11 or later is required.

func (NamespaceFd) Parent

func (nsfd NamespaceFd) Parent() (relations.Relation, error)

Parent returns the parent namespace of the Linux-kernel namespace referenced by this open file descriptor. The namespace references must be either of type PID or user. For user namespaces, NamespaceFd.Parent and NamespaceFd.User mostly behave identical, except that NamespaceFd.Parent returns an untyped NamespaceFile reference, whereas NamespaceFd.User returns a TypedNamespaceFile instead.

ℹ️ A Linux kernel version 4.9 or later is required.

func (NamespaceFd) String

func (nsfd NamespaceFd) String() string

String returns the textual representation for a namespace reference by file descriptor. This does contain only the file descriptor, but not the referenced namespace (ID), as we're here dealing with the references themselves.

func (NamespaceFd) Type

func (nsfd NamespaceFd) Type() (species.NamespaceType, error)

Type returns the type of the Linux-kernel namespace referenced by this open file descriptor.

🛈 A Linux kernel version 4.11 or later is required.

func (NamespaceFd) User

func (nsfd NamespaceFd) User() (relations.Relation, error)

User returns the owning user namespace the namespace referenced by this open file descriptor. The owning user namespace is returned in form of a TypedNamespaceFile reference. For user namespaces, User (mostly) behaves identical to NamespaceFd.Parent. The only difference is that User returns a TypedNamespaceFile, whereas NamespaceFd.Parent returns an untyped NamespaceFile reference.

🛈 A Linux kernel version 4.9 or later is required.

type NamespaceFile

type NamespaceFile struct {
	// Please note that we embed(!) an os.File instead of embedding an *os.File.
	// Our rationale here is that this is fine, as os.File has an indirection
	// designed in anyway in order to avoid users of os.File overwriting the
	// file descriptors. With this indirection in mind, we simply skip yet
	// another level of indirection, hopefully reducing pointer chasing. Hold my
	// beer!
	os.File
}

NamespaceFile is an open os.File which references a Linux-kernel namespace. Please use NewNamespaceFile to create a *NamespaceFile from an *os.File and an error (correctly deals with errors by returning a nil *NamespaceFile).

func NewNamespaceFile

func NewNamespaceFile(f *os.File, err error) (*NamespaceFile, error)

NewNamespaceFile returns a new NamespaceFile given an *os.File and a nil error. In case of a non-nil error or of a nil *os.File it returns a nil *NamespaceFile instead, together with the error. This “constructor” is intended to be conveniently used with os.Open by directly accepting its two return values.

func (NamespaceFile) ID

func (nsf NamespaceFile) ID() (species.NamespaceID, error)

ID returns the namespace ID in form of its inode number for any given Linux kernel namespace reference.

func (NamespaceFile) NsFd

func (nsf NamespaceFile) NsFd() (int, opener.FdCloser, error)

NsFd returns a file descriptor referencing the namespace indicated in a namespace reference implementing the opener.Opener interface.

⚠️ After the caller is done using the returned file descriptor, the caller must call the returned opener.FdCloser function in order to properly release process resources. In case of any error when opening the referenced namespace, err will be non-nil, and might additionally wrap an underlying OS-level error.

⚠️ The caller must make sure that the namespace reference object doesn't get prematurely garbage collected, while the file descriptor returned by NsFd is still in use.

func (NamespaceFile) OpenTypedReference

func (nsf NamespaceFile) OpenTypedReference() (relations.Relation, opener.ReferenceCloser, error)

OpenTypedReference returns an open namespace reference, from which an OS-level file descriptor can be retrieved using NamespaceFile.NsFd.

OpenTypeReference is also internally used to allow optimizing switching namespaces under the condition that additionally the type of namespace needs to be known at the same time.

func (NamespaceFile) OwnerUID

func (nsf NamespaceFile) OwnerUID() (int, error)

OwnerUID returns the user id (UID) of the user namespace referenced by this open file descriptor.

🛈 A Linux kernel version 4.11 or later is required.

func (NamespaceFile) Parent

func (nsf NamespaceFile) Parent() (relations.Relation, error)

Parent returns the parent namespace of a hierarchical namespaces, that is, of PID and user namespaces. For user namespaces, NamespaceFile.Parent and NamespaceFile.User mostly behave identical, except that the latter returns a TypedNamespaceFile, while Parent returns an untyped NamespaceFile instead.

🛈 A Linux kernel version 4.9 or later is required.

func (NamespaceFile) String

func (nsf NamespaceFile) String() (s string)

String returns the textual representation for a namespace reference by file. This does contain only the os.File, but not the referenced namespace (ID), as we're here dealing with the references themselves.

func (NamespaceFile) Type

func (nsf NamespaceFile) Type() (species.NamespaceType, error)

Type returns the type of the Linux-kernel namespace referenced by this open file.

🛈 A Linux kernel version 4.11 or later is required.

func (NamespaceFile) User

func (nsf NamespaceFile) User() (relations.Relation, error)

User returns the owning user namespace of any namespace, as a NamespaceFile reference. For user namespaces, NamespaceFile.User mostly behaves identical to NamespaceFile.Parent, except that the latter returns an untyped NamespaceFile instead of a TypedNamespaceFile.

🛈 A Linux kernel version 4.9 or later is required.

type NamespaceOperationError

type NamespaceOperationError struct {
	InvalidNamespaceError
	Op string // failed namespace ioctl operation
}

NamespaceOperationError wraps an invalid namespace operation (in form of an InvalidNamespaceError), giving information about the failed operation both on a high level, as well as the underlying invalid namespace and OS-level errors.

func (*NamespaceOperationError) Error

func (e *NamespaceOperationError) Error() string

Error returns a textual description of this invalid namespace error.

type NamespacePath

type NamespacePath string

NamespacePath references a Linux-kernel namespace via a filesystem path.

func (NamespacePath) ID

func (nsp NamespacePath) ID() (species.NamespaceID, error)

ID returns the namespace ID in form of its inode number for any given Linux kernel namespace reference.

func (NamespacePath) NsFd

func (nsp NamespacePath) NsFd() (int, opener.FdCloser, error)

NsFd returns a file descriptor referencing the namespace indicated in a namespace reference implementing the Opener interface.

⚠️ After the caller is done using the returned file descriptor, the caller must call the returned opener.FdCloser function in order to properly release process resources. In case of any error when opening the referenced namespace, err will be non-nil, and might additionally wrap an underlying OS-level error.

⚠️ The caller must make sure that the namespace reference object doesn't get prematurely garbage collected, while the file descriptor returned by NamespacePath.NsFd is still in use.

func (NamespacePath) OpenTypedReference

func (nsp NamespacePath) OpenTypedReference() (relations.Relation, opener.ReferenceCloser, error)

OpenTypedReference returns an open namespace reference, from which an OS-level file descriptor can be retrieved using NamespacePath.NsFd.

OpenTypeReference is also internally used to allow optimizing switching namespaces under the condition that additionally the type of namespace needs to be known at the same time.

func (NamespacePath) OwnerUID

func (nsp NamespacePath) OwnerUID() (int, error)

OwnerUID returns the user id (UID) of the user namespace referenced by this open file descriptor.

🛈 A Linux kernel version 4.11 or later is required.

func (NamespacePath) Parent

func (nsp NamespacePath) Parent() (relations.Relation, error)

Parent returns the parent namespace of a hierarchical namespaces, that is, of PID and user namespaces. For user namespaces, NamespaceFile.Parent and NamespaceFile.User mostly behave identical, except that the latter returns a TypedNamespaceFile, while Parent returns an untyped NamespaceFile instead.

🛈 A Linux kernel version 4.9 or later is required.

func (NamespacePath) String

func (nsp NamespacePath) String() string

String returns the textual representation for a namespace reference by file descriptor. This does contain only the file descriptor, but not the referenced namespace (ID), as we're here dealing with the references themselves.

func (NamespacePath) Type

func (nsp NamespacePath) Type() (species.NamespaceType, error)

Type returns the type of the Linux-kernel namespace referenced by this file path.

🛈 A Linux kernel version 4.11 or later is required.

func (NamespacePath) User

func (nsp NamespacePath) User() (relations.Relation, error)

User returns the owning user namespace of any namespace, as a NamespaceFile reference. For user namespaces, NamespaceFile.User mostly behaves identical to NamespaceFile.Parent, except that the latter returns an untyped NamespaceFile instead of a TypedNamespaceFile.

🛈 A Linux kernel version 4.9 or later is required.

type RestoreNamespaceErr

type RestoreNamespaceErr struct {
	// contains filtered or unexported fields
}

RestoreNamespaceErr represents an error that occurred while trying to restore the original namespaces after Visit'ing a function with (some) switched Linux-kernel namespaces.

Check for the occurrence of an RestoreNamespaceError using errors.As, for instance:

err := Visit(fn, nsrefs)
var nserr RestoreNamespaceError
if errors.As(err, &nserr) {
       ...
}

func (*RestoreNamespaceErr) Error

func (e *RestoreNamespaceErr) Error() string

Error returns the detailed error message when restoring a namespace failed.

func (*RestoreNamespaceErr) Unwrap

func (e *RestoreNamespaceErr) Unwrap() error

Unwrap returns the wrapped error giving details about why restoring a namespace failed.

type SwitchNamespaceErr

type SwitchNamespaceErr struct {
	// contains filtered or unexported fields
}

SwitchNamespaceErr represents an error that occurred while trying to switch into a different namespace.

Check for the occurrence of an SwitchNamespaceErr using errors.As, for instance:

err := Visit(fn, nsrefs)
var nserr SwitchNamespaceErr
if errors.As(err, &nserr) {
    ...
}

func (*SwitchNamespaceErr) Error

func (e *SwitchNamespaceErr) Error() string

Error returns the detailed error message when switching into a namespace failed.

func (*SwitchNamespaceErr) Unwrap

func (e *SwitchNamespaceErr) Unwrap() error

Unwrap returns the wrapped error giving details about why switching into a namespace failed.

type TypedNamespaceFd

type TypedNamespaceFd struct {
	NamespaceFd // underlying file descriptor referencing the namespace.
	// contains filtered or unexported fields
}

TypedNamespaceFd references a Linux-kernel namespace via an open file descriptor.

func NewTypedNamespaceFd

func NewTypedNamespaceFd(fd int, nstype species.NamespaceType) (*TypedNamespaceFd, error)

NewTypedNamespaceFd wraps a OS-level file descriptor referencing a Linux-kernel namespace, as well as the type of namespace. NewTypedNamespaceFd can be used in those situations where the type of namespace is already known, where later access to the type is of reference required and the namespace type query ioctl() is to be avoided (such as to support 4.9 to pre-4.11 Linux kernels).

func (*TypedNamespaceFd) OpenTypedReference

func (nsfd *TypedNamespaceFd) OpenTypedReference() (relations.Relation, opener.ReferenceCloser, error)

OpenTypedReference returns an open namespace reference, from which an OS-level file descriptor can be retrieved using [TypedNamespaceFd.NsFd].

OpenTypeReference is also internally used to allow optimizing switching namespaces under the condition that additionally the type of namespace needs to be known at the same time.

func (TypedNamespaceFd) String

func (nsfd TypedNamespaceFd) String() string

String returns the textual representation for a typed namespace reference by file descriptor. This does contain only the type as well as the file descriptor, but not the referenced namespace (ID), as we're here dealing with the references themselves. If a dedicated reference was given at creation time (such as a filesystem path), then this is used instead of the fd number.

func (TypedNamespaceFd) Type

Type returns the foreknown type of the Linux-kernel namespace set when this namespace reference was created. This avoids having to call the corresponding namespace-type syscall, so it will work also on Linux kernels before 4.11, offering limited backward supported in those situations where the type of namespace is already known when establishing the namespace reference.

type TypedNamespaceFile

type TypedNamespaceFile struct {
	NamespaceFile
	// contains filtered or unexported fields
}

TypedNamespaceFile is a NamespaceFile (wrapping an open os.File) with a foreknown namespace type, optionally to be used in those use cases where the type of namespace referenced is known in advance. In such cases, ioctl() round trips to infer the type of namespace (when required) can be avoided, using the foreknown type instead.

func NewTypedNamespaceFile

func NewTypedNamespaceFile(f *os.File, nstype species.NamespaceType) (*TypedNamespaceFile, error)

NewTypedNamespaceFile takes an open(!) *os.File plus the type of namespace referenced and returns a new typed namespace reference object. If the namespace type is left zero, then this convenience helper will auto-detect it, unless when on a pre-4.11 kernel, where auto-detection is impossible due to the missing specific ioctl().

func (TypedNamespaceFile) NsFd

func (nsf TypedNamespaceFile) NsFd() (int, opener.FdCloser, error)

NsFd returns a file descriptor referencing the namespace indicated in a namespace reference implementing the Opener interface.

⚠️ After the caller is done using the returned file descriptor, the caller must call the returned FdCloser function in order to properly release process resources. In case of any error when opening the referenced namespace, err will be non-nil, and might additionally wrap an underlying OS-level error.

⚠️ The caller must make sure that the namespace reference object doesn't get prematurely garbage collected, while the file descriptor returned by NsFd() is still in use.

func (TypedNamespaceFile) OpenTypedReference

func (nsf TypedNamespaceFile) OpenTypedReference() (relations.Relation, opener.ReferenceCloser, error)

OpenTypedReference returns an open and typed namespace reference, from which an OS-level file descriptor can be retrieved using TypedNamespaceFile.NsFd. OpenTypeReference is internally used to allow optimizing switching namespaces under the condition that additionally the type of namespace needs to be known at the same time.

func (TypedNamespaceFile) Parent

func (nsf TypedNamespaceFile) Parent() (relations.Relation, error)

Parent returns the parent namespace of a hierarchical namespaces, that is, of PID and user namespaces. For user namespaces, Parent() and User() behave identical.

ℹ️ A Linux kernel version 4.9 or later is required.

func (TypedNamespaceFile) Type

Type returns the foreknown type of the Linux-kernel namespace set when this namespace reference was created. This avoids having to call the corresponding namespace-type syscall, so it will work also on Linux kernels before 4.11, offering limited backward supported in those situations where the type of namespace is already known when establishing the namespace reference.

type TypedNamespacePath

type TypedNamespacePath struct {
	NamespacePath
	// contains filtered or unexported fields
}

TypedNamespacePath is an explicitly typed NamespacePath reference in the file system. Use this type in case you (1) need to use Visit(), AND (2) must support kernels pre-4.11 which lack support for the NS_GET_NSTYPE ioctl(), AND (3) you know already the specific type of namespace. You may also use TypedNamespacePath when using Visit() on newer kernels to slightly optimize things, but this isn't strictly necessary.

🛈 Please note that [TypedNamespacePath.User] and TypedNamespacePath.Parent require a least a 4.9+ kernel. OwnerUID() requires at least a 4.11+ kernel.

func NewTypedNamespacePath

func NewTypedNamespacePath(path string, nstype species.NamespaceType) *TypedNamespacePath

NewTypedNamespacePath returns a new explicitly typed namespace path reference.

func (TypedNamespacePath) OpenTypedReference

func (nsp TypedNamespacePath) OpenTypedReference() (relations.Relation, opener.ReferenceCloser, error)

OpenTypedReference returns an open namespace reference, from which an OS-level file descriptor can be retrieved using NsFd(). OpenTypeReference is internally used to allow optimizing switching namespaces under the condition that additionally the type of namespace needs to be known at the same time.

func (TypedNamespacePath) Parent

func (nsp TypedNamespacePath) Parent() (relations.Relation, error)

Parent returns the parent namespace of a hierarchical namespaces, that is, of PID and user namespaces. For user namespaces, TypedNamespacePath.Parent and [TypedNamespacePath.User] mostly behave identical, except that the latter returns a TypedNamespaceFile, while Parent returns an untyped NamespaceFile instead.

🛈 A Linux kernel version 4.9 or later is required.

func (TypedNamespacePath) String

func (nsp TypedNamespacePath) String() string

String returns the textual representation for a namespace reference by file descriptor. This does contain only the file descriptor, but not the referenced namespace (ID), as we're here dealing with the references themselves.

func (TypedNamespacePath) Type

Type returns the foreknown type of the Linux-kernel namespace set when this namespace reference was created. This avoids having to call the corresponding namespace-type syscall, so it will work also on Linux kernels before 4.11, offering limited backward supported in those situations where the type of namespace is already known when establishing the namespace reference.

Directories

Path Synopsis
internal
opener
Package opener provides access to the file descriptors of namespace references.
Package opener provides access to the file descriptors of namespace references.
Package mountineer allows accessing the file system contents from (other) mount namespaces via procfs.
Package mountineer allows accessing the file system contents from (other) mount namespaces via procfs.
mntnssandbox
Package mntnssandbox is a single-purpose, stripped-down version of thediveo/gons.
Package mntnssandbox is a single-purpose, stripped-down version of thediveo/gons.
Package portable provides so-called “portable” namespace references with validation and “locking” to the referenced namespace open and thus alive.
Package portable provides so-called “portable” namespace references with validation and “locking” to the referenced namespace open and thus alive.
Package relations gives access to properties of and relationships between Linux-kernel namespaces, such as type and ID of a namespace, its owning user namespace, parent namespace in case of hierarchical namespaces, et cetera.
Package relations gives access to properties of and relationships between Linux-kernel namespaces, such as type and ID of a namespace, its owning user namespace, parent namespace in case of hierarchical namespaces, et cetera.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL