github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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/snap"
    41  	"github.com/snapcore/snapd/timings"
    42  )
    43  
    44  // Backend is responsible for maintaining mount files for snap-confine
    45  type Backend struct{}
    46  
    47  // Initialize does nothing.
    48  func (b *Backend) Initialize() error {
    49  	return nil
    50  }
    51  
    52  // Name returns the name of the backend.
    53  func (b *Backend) Name() interfaces.SecuritySystem {
    54  	return interfaces.SecurityMount
    55  }
    56  
    57  // Setup creates mount mount profile files specific to a given snap.
    58  func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error {
    59  	// Record all changes to the mount system for this snap.
    60  	snapName := snapInfo.InstanceName()
    61  	spec, err := repo.SnapSpecification(b.Name(), snapName)
    62  	if err != nil {
    63  		return fmt.Errorf("cannot obtain mount security snippets for snap %q: %s", snapName, err)
    64  	}
    65  	spec.(*Specification).AddOvername(snapInfo)
    66  	spec.(*Specification).AddLayout(snapInfo)
    67  	content := deriveContent(spec.(*Specification), snapInfo)
    68  	// synchronize the content with the filesystem
    69  	glob := fmt.Sprintf("snap.%s.*fstab", snapName)
    70  	dir := dirs.SnapMountPolicyDir
    71  	if err := os.MkdirAll(dir, 0755); err != nil {
    72  		return fmt.Errorf("cannot create directory for mount configuration files %q: %s", dir, err)
    73  	}
    74  	if _, _, err := osutil.EnsureDirState(dir, glob, content); err != nil {
    75  		return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err)
    76  	}
    77  	if err := UpdateSnapNamespace(snapName); err != nil {
    78  		return fmt.Errorf("cannot update mount namespace of snap %q: %s", snapName, err)
    79  	}
    80  	return nil
    81  }
    82  
    83  // Remove removes mount configuration files of a given snap.
    84  //
    85  // This method should be called after removing a snap.
    86  func (b *Backend) Remove(snapName string) error {
    87  	glob := fmt.Sprintf("snap.%s.*fstab", snapName)
    88  	_, _, err := osutil.EnsureDirState(dirs.SnapMountPolicyDir, glob, nil)
    89  	if err != nil {
    90  		return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err)
    91  	}
    92  	return DiscardSnapNamespace(snapName)
    93  }
    94  
    95  // addMountProfile adds a mount profile with the given name, based on the given entries.
    96  //
    97  // If there are no entries no profile is generated.
    98  func addMountProfile(content map[string]*osutil.FileState, fname string, entries []osutil.MountEntry) {
    99  	if len(entries) == 0 {
   100  		return
   101  	}
   102  	var buffer bytes.Buffer
   103  	for _, entry := range entries {
   104  		fmt.Fprintf(&buffer, "%s\n", entry)
   105  	}
   106  	content[fname] = &osutil.FileState{Content: buffer.Bytes(), Mode: 0644}
   107  }
   108  
   109  // deriveContent computes .fstab tables based on requests made to the specification.
   110  func deriveContent(spec *Specification, snapInfo *snap.Info) map[string]*osutil.FileState {
   111  	content := make(map[string]*osutil.FileState, 2)
   112  	snapName := snapInfo.InstanceName()
   113  	// Add the per-snap fstab file.
   114  	// This file is read by snap-update-ns in the global pass.
   115  	addMountProfile(content, fmt.Sprintf("snap.%s.fstab", snapName), spec.MountEntries())
   116  	// Add the per-snap user-fstab file.
   117  	// This file will be read by snap-update-ns in the per-user pass.
   118  	addMountProfile(content, fmt.Sprintf("snap.%s.user-fstab", snapName), spec.UserMountEntries())
   119  	return content
   120  }
   121  
   122  // NewSpecification returns a new mount specification.
   123  func (b *Backend) NewSpecification() interfaces.Specification {
   124  	return &Specification{}
   125  }
   126  
   127  // SandboxFeatures returns the list of features supported by snapd for composing mount namespaces.
   128  func (b *Backend) SandboxFeatures() []string {
   129  	return []string{
   130  		"freezer-cgroup-v1",       /* Snapd creates a freezer cgroup (v1) for each snap */
   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  }