github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/interfaces/mount/backend.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  // Package mount implements mounts that get mapped into the snap
    21  //
    22  // Snappy creates fstab like  configuration files that describe what
    23  // directories from the system or from other snaps should get mapped
    24  // into the snap.
    25  //
    26  // Each fstab like file looks like a regular fstab entry:
    27  //   /src/dir /dst/dir none bind 0 0
    28  //   /src/dir /dst/dir none bind,rw 0 0
    29  // but only bind mounts are supported
    30  package mount
    31  
    32  import (
    33  	"bytes"
    34  	"fmt"
    35  	"os"
    36  
    37  	"github.com/snapcore/snapd/dirs"
    38  	"github.com/snapcore/snapd/interfaces"
    39  	"github.com/snapcore/snapd/osutil"
    40  	"github.com/snapcore/snapd/sandbox/cgroup"
    41  	"github.com/snapcore/snapd/snap"
    42  	"github.com/snapcore/snapd/timings"
    43  )
    44  
    45  // Backend is responsible for maintaining mount files for snap-confine
    46  type Backend struct{}
    47  
    48  // Initialize does nothing.
    49  func (b *Backend) Initialize(*interfaces.SecurityBackendOptions) error {
    50  	return nil
    51  }
    52  
    53  // Name returns the name of the backend.
    54  func (b *Backend) Name() interfaces.SecuritySystem {
    55  	return interfaces.SecurityMount
    56  }
    57  
    58  // Setup creates mount mount profile files specific to a given snap.
    59  func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error {
    60  	// Record all changes to the mount system for this snap.
    61  	snapName := snapInfo.InstanceName()
    62  	spec, err := repo.SnapSpecification(b.Name(), snapName)
    63  	if err != nil {
    64  		return fmt.Errorf("cannot obtain mount security snippets for snap %q: %s", snapName, err)
    65  	}
    66  	spec.(*Specification).AddOvername(snapInfo)
    67  	spec.(*Specification).AddLayout(snapInfo)
    68  	content := deriveContent(spec.(*Specification), snapInfo)
    69  	// synchronize the content with the filesystem
    70  	glob := fmt.Sprintf("snap.%s.*fstab", snapName)
    71  	dir := dirs.SnapMountPolicyDir
    72  	if err := os.MkdirAll(dir, 0755); err != nil {
    73  		return fmt.Errorf("cannot create directory for mount configuration files %q: %s", dir, err)
    74  	}
    75  	if _, _, err := osutil.EnsureDirState(dir, glob, content); err != nil {
    76  		return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err)
    77  	}
    78  	if err := UpdateSnapNamespace(snapName); err != nil {
    79  		return fmt.Errorf("cannot update mount namespace of snap %q: %s", snapName, err)
    80  	}
    81  	return nil
    82  }
    83  
    84  // Remove removes mount configuration files of a given snap.
    85  //
    86  // This method should be called after removing a snap.
    87  func (b *Backend) Remove(snapName string) error {
    88  	glob := fmt.Sprintf("snap.%s.*fstab", snapName)
    89  	_, _, err := osutil.EnsureDirState(dirs.SnapMountPolicyDir, glob, nil)
    90  	if err != nil {
    91  		return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err)
    92  	}
    93  	return DiscardSnapNamespace(snapName)
    94  }
    95  
    96  // addMountProfile adds a mount profile with the given name, based on the given entries.
    97  //
    98  // If there are no entries no profile is generated.
    99  func addMountProfile(content map[string]osutil.FileState, fname string, entries []osutil.MountEntry) {
   100  	if len(entries) == 0 {
   101  		return
   102  	}
   103  	var buffer bytes.Buffer
   104  	for _, entry := range entries {
   105  		fmt.Fprintf(&buffer, "%s\n", entry)
   106  	}
   107  	content[fname] = &osutil.MemoryFileState{Content: buffer.Bytes(), Mode: 0644}
   108  }
   109  
   110  // deriveContent computes .fstab tables based on requests made to the specification.
   111  func deriveContent(spec *Specification, snapInfo *snap.Info) map[string]osutil.FileState {
   112  	content := make(map[string]osutil.FileState, 2)
   113  	snapName := snapInfo.InstanceName()
   114  	// Add the per-snap fstab file.
   115  	// This file is read by snap-update-ns in the global pass.
   116  	addMountProfile(content, fmt.Sprintf("snap.%s.fstab", snapName), spec.MountEntries())
   117  	// Add the per-snap user-fstab file.
   118  	// This file will be read by snap-update-ns in the per-user pass.
   119  	addMountProfile(content, fmt.Sprintf("snap.%s.user-fstab", snapName), spec.UserMountEntries())
   120  	return content
   121  }
   122  
   123  // NewSpecification returns a new mount specification.
   124  func (b *Backend) NewSpecification() interfaces.Specification {
   125  	return &Specification{}
   126  }
   127  
   128  // SandboxFeatures returns the list of features supported by snapd for composing mount namespaces.
   129  func (b *Backend) SandboxFeatures() []string {
   130  	commonFeatures := []string{
   131  		"layouts",                 /* Mount profiles take layout data into account */
   132  		"mount-namespace",         /* Snapd creates a mount namespace for each snap */
   133  		"per-snap-persistency",    /* Per-snap profiles are persisted across invocations */
   134  		"per-snap-profiles",       /* Per-snap profiles allow changing mount namespace of a given snap */
   135  		"per-snap-updates",        /* Changes to per-snap mount profiles are applied instantly */
   136  		"per-snap-user-profiles",  /* Per-snap profiles allow changing mount namespace of a given snap for a given user */
   137  		"stale-base-invalidation", /* Mount namespaces that go stale because base snap changes are automatically invalidated */
   138  	}
   139  	cgroupv1Features := []string{
   140  		"freezer-cgroup-v1", /* Snapd creates a freezer cgroup (v1) for each snap */
   141  	}
   142  
   143  	if cgroup.IsUnified() {
   144  		// TODO: update we get feature parity on cgroup v2
   145  		return commonFeatures
   146  	}
   147  
   148  	features := append(commonFeatures, cgroupv1Features...)
   149  	return features
   150  }