// Copyright 2026 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 fscheckpoint defines the format of a gVisor filesystem checkpoint.
//
// Filesystem checkpoints can only be taken in sandboxes with runsc flag
// `-overlay2` enabled, which causes sandboxed filesystems to be overlaid with
// a tmpfs "upper layer" that captures filesystem changes. Each filesystem
// checkpoint contains the state of all upper layers in the sandbox.
//
// A filesystem checkpoint comprises four files:
//
//   - A "pages metadata" file and a "pages" file are generated by
//     pgalloc.MemoryFile save/restore. These contain the state of MemoryFiles
//     that store the contents of files in checkpointed tmpfs filesystems.
//
//   - A "multi-tar" file contains tar archives representing the filesystem
//     structure of checkpointed tmpfs filesystems. For regular files, these
//     tar archives store the MemoryFile offsets containing file data, avoiding
//     duplication of MemoryFile content and ensuring a dense representation of
//     possibly-sparse files.
//
//   - A "manifest" file contains information about each MemoryFile and tmpfs
//     filesystem required to load state from other checkpoint files. See type
//     definitions below.
//
// TODO: NOLINT - The tar file format involves a lot of padding to 512-byte
// boundaries. Consider gzip/zstd compression of (each archive in) the
// multi-tar file, as well as something simpler like run-length encoding of
// zero bytes.
package fscheckpoint

import (
	"gvisor.dev/gvisor/pkg/sentry/checkpoint"
)

// AllTmpfsPath is a sentinel value for the Path option of a filesystem
// checkpoint save (kernel.FSSaveOpts.Path) indicating that all tmpfs
// filesystems with a ResourceID should be checkpointed, rather than only the
// tmpfs filesystems mounted at a specific path.
const AllTmpfsPath = "all-tmpfs"

// Manifest is the type of the JSON object stored in the manifest file.
type Manifest struct {
	// Version is the checkpoint format version.
	//
	// While Version is 0, filesystem checkpoint compatibility is not
	// guaranteed between differing runsc binary versions, so RunscVersion is
	// used to check compatibility. Version should not be incremented to 1
	// until filesystem checkpoint compatibility is established.
	Version uint64 `json:"version,omitzero"`

	// RunscVersion is the version of the runsc binary that produced this
	// checkpoint.
	//
	// TODO: NOLINT - Currently this must match during restore. Removing this
	// requirement involves at least stabilizing pgalloc's checkpoint format.
	RunscVersion string `json:"runsc_version,omitzero"`

	// PageSize is hostarch.PageSize for the sandbox that produced this
	// checkpoint.
	//
	// TODO: NOLINT - Currently this must match during restore. Technically,
	// it's already permissible for PageSize to shrink (as long as both old and
	// new sizes remain powers of 2). Allowing PageSize to increase may require
	// being able to relocate pgalloc.MemoryFile pages during restore based on
	// information provided by tmpfs.
	PageSize uint64 `json:"page_size"`

	// Endian is hostarch.EndianString() for the sandbox that produced this
	// checkpoint.
	//
	// TODO: NOLINT - Currently this must match during restore. Removing this
	// requirement requires fixing an endianness for
	// tmpfs.fsckptRegularFileSegment, and for pgalloc checkpoints.
	Endian string `json:"endian"`

	// MemoryFiles contains information about pgalloc.MemoryFiles stored in the
	// checkpoint, in order of increasing file offsets in the pages metadata
	// and pages files.
	MemoryFiles []MemoryFile `json:"memory_files"`

	// Information about filesystems stored in the checkpoint, in order of
	// increasing file offsets in the multi-tar file.
	Tmpfs []Tmpfs `json:"tmpfs"`
}

// MemoryFile represents a pgalloc.MemoryFile stored in a filesystem
// checkpoint.
type MemoryFile struct {
	// ResourceID is the value of pgalloc.MemoryFile.ResourceID().
	ResourceID checkpoint.ResourceID `json:"resource_id"`

	// PagesMetadataStart and PagesMetadataEnd are the offsets in the pages
	// metadata file at which the pages metadata for this MemoryFile begin and
	// end respectively.
	PagesMetadataStart uint64 `json:"pages_metadata_start"`
	PagesMetadataEnd   uint64 `json:"pages_metadata_end"`

	// PagesStart is the offset in the pages file at which the pages for this
	// MemoryFile begin.
	PagesStart uint64 `json:"pages_start"`
}

// Tmpfs represents a tmpfs filesystem stored in a filesystem checkpoint.
type Tmpfs struct {
	// ResourceID is the value of tmpfs.filesystem.mf.ResourceID().
	//
	// TODO: NOLINT - Assign ResourceIDs to tmpfs filesystems directly so that
	// we can also checkpoint filesystems using the main MemoryFile.
	ResourceID checkpoint.ResourceID `json:"resource_id"`

	// TarStart and TarEnd are the offsets in the multi-tar file at which the
	// tar archive for this filesystem begin and end respectively.
	TarStart uint64 `json:"tar_start"`
	TarEnd   uint64 `json:"tar_end"`
}
