// namespaces provides Linux namespace introspection for CRIU checkpoint. package checkpoint import ( "fmt" "golang.org/x/sys/unix" ) // NamespaceManifestEntry stores namespace information saved in checkpoint manifests. type NamespaceManifestEntry struct { Type string `yaml:"type"` // net, pid, mnt, etc. Inode uint64 `yaml:"inode"` // Namespace inode IsExternal bool `yaml:"isExternal"` // Whether namespace is external (shared) } // NamespaceType represents a Linux namespace type type NamespaceType string const ( NamespaceNet NamespaceType = "net" NamespacePID NamespaceType = "pid" NamespaceMnt NamespaceType = "mnt" NamespaceUTS NamespaceType = "uts" NamespaceIPC NamespaceType = "ipc" NamespaceUser NamespaceType = "user" NamespaceCgroup NamespaceType = "cgroup" ) // NamespaceInfo holds namespace identification information type NamespaceInfo struct { Type NamespaceType Inode uint64 IsExternal bool // Whether NS is external (shared with pause container) } // NewNamespaceManifestEntries constructs namespace manifest entries from introspected namespaces. func NewNamespaceManifestEntries(namespaces map[NamespaceType]*NamespaceInfo) []NamespaceManifestEntry { if len(namespaces) == 0 { return nil } result := make([]NamespaceManifestEntry, 0, len(namespaces)) for nsType, nsInfo := range namespaces { result = append(result, NamespaceManifestEntry{ Type: string(nsType), Inode: nsInfo.Inode, IsExternal: nsInfo.IsExternal, }) } return result } // GetNamespaceInode returns the inode number for a namespace func GetNamespaceInode(pid int, nsType NamespaceType) (uint64, error) { nsPath := fmt.Sprintf("%s/%d/ns/%s", HostProcPath, pid, nsType) var stat unix.Stat_t if err := unix.Stat(nsPath, &stat); err != nil { return 0, fmt.Errorf("failed to stat namespace %s: %w", nsPath, err) } return stat.Ino, nil } // GetNamespaceInfo returns detailed namespace information func GetNamespaceInfo(pid int, nsType NamespaceType) (*NamespaceInfo, error) { nsPath := fmt.Sprintf("%s/%d/ns/%s", HostProcPath, pid, nsType) // Get inode var stat unix.Stat_t if err := unix.Stat(nsPath, &stat); err != nil { return nil, fmt.Errorf("failed to stat namespace %s: %w", nsPath, err) } // Check if this is different from init's namespace (PID 1) initNsPath := fmt.Sprintf("%s/1/ns/%s", HostProcPath, nsType) var initStat unix.Stat_t isExternal := false if err := unix.Stat(initNsPath, &initStat); err == nil { // If the inode is different from init's, it's an external namespace isExternal = stat.Ino != initStat.Ino } return &NamespaceInfo{ Type: nsType, Inode: stat.Ino, IsExternal: isExternal, }, nil } // GetAllNamespaces returns information about all namespaces for a process func GetAllNamespaces(pid int) (map[NamespaceType]*NamespaceInfo, error) { nsTypes := []NamespaceType{ NamespaceNet, NamespacePID, NamespaceMnt, NamespaceUTS, NamespaceIPC, NamespaceUser, NamespaceCgroup, } namespaces := make(map[NamespaceType]*NamespaceInfo) for _, nsType := range nsTypes { if info, err := GetNamespaceInfo(pid, nsType); err == nil { namespaces[nsType] = info } } return namespaces, nil }