gitee.com/mysnapcore/mysnapd@v0.1.0/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  	"gitee.com/mysnapcore/mysnapd/dirs"
    38  	"gitee.com/mysnapcore/mysnapd/interfaces"
    39  	"gitee.com/mysnapcore/mysnapd/osutil"
    40  	"gitee.com/mysnapcore/mysnapd/sandbox/cgroup"
    41  	"gitee.com/mysnapcore/mysnapd/snap"
    42  	"gitee.com/mysnapcore/mysnapd/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  	spec.(*Specification).AddExtraLayouts(confinement.ExtraLayouts)
    69  	content := deriveContent(spec.(*Specification), snapInfo)
    70  	// synchronize the content with the filesystem
    71  	glob := fmt.Sprintf("snap.%s.*fstab", snapName)
    72  	dir := dirs.SnapMountPolicyDir
    73  	if err := os.MkdirAll(dir, 0755); err != nil {
    74  		return fmt.Errorf("cannot create directory for mount configuration files %q: %s", dir, err)
    75  	}
    76  	if _, _, err := osutil.EnsureDirState(dir, glob, content); err != nil {
    77  		return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err)
    78  	}
    79  	if err := UpdateSnapNamespace(snapName); err != nil {
    80  		return fmt.Errorf("cannot update mount namespace of snap %q: %s", snapName, err)
    81  	}
    82  	return nil
    83  }
    84  
    85  // Remove removes mount configuration files of a given snap.
    86  //
    87  // This method should be called after removing a snap.
    88  func (b *Backend) Remove(snapName string) error {
    89  	glob := fmt.Sprintf("snap.%s.*fstab", snapName)
    90  	_, _, err := osutil.EnsureDirState(dirs.SnapMountPolicyDir, glob, nil)
    91  	if err != nil {
    92  		return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err)
    93  	}
    94  	return DiscardSnapNamespace(snapName)
    95  }
    96  
    97  // addMountProfile adds a mount profile with the given name, based on the given entries.
    98  //
    99  // If there are no entries no profile is generated.
   100  func addMountProfile(content map[string]osutil.FileState, fname string, entries []osutil.MountEntry) {
   101  	if len(entries) == 0 {
   102  		return
   103  	}
   104  	var buffer bytes.Buffer
   105  	for _, entry := range entries {
   106  		fmt.Fprintf(&buffer, "%s\n", entry)
   107  	}
   108  	content[fname] = &osutil.MemoryFileState{Content: buffer.Bytes(), Mode: 0644}
   109  }
   110  
   111  // deriveContent computes .fstab tables based on requests made to the specification.
   112  func deriveContent(spec *Specification, snapInfo *snap.Info) map[string]osutil.FileState {
   113  	content := make(map[string]osutil.FileState, 2)
   114  	snapName := snapInfo.InstanceName()
   115  	// Add the per-snap fstab file.
   116  	// This file is read by snap-update-ns in the global pass.
   117  	addMountProfile(content, fmt.Sprintf("snap.%s.fstab", snapName), spec.MountEntries())
   118  	// Add the per-snap user-fstab file.
   119  	// This file will be read by snap-update-ns in the per-user pass.
   120  	addMountProfile(content, fmt.Sprintf("snap.%s.user-fstab", snapName), spec.UserMountEntries())
   121  	return content
   122  }
   123  
   124  // NewSpecification returns a new mount specification.
   125  func (b *Backend) NewSpecification() interfaces.Specification {
   126  	return &Specification{}
   127  }
   128  
   129  // SandboxFeatures returns the list of features supported by snapd for composing mount namespaces.
   130  func (b *Backend) SandboxFeatures() []string {
   131  	commonFeatures := []string{
   132  		"layouts",                 /* Mount profiles take layout data into account */
   133  		"mount-namespace",         /* Snapd creates a mount namespace for each snap */
   134  		"per-snap-persistency",    /* Per-snap profiles are persisted across invocations */
   135  		"per-snap-profiles",       /* Per-snap profiles allow changing mount namespace of a given snap */
   136  		"per-snap-updates",        /* Changes to per-snap mount profiles are applied instantly */
   137  		"per-snap-user-profiles",  /* Per-snap profiles allow changing mount namespace of a given snap for a given user */
   138  		"stale-base-invalidation", /* Mount namespaces that go stale because base snap changes are automatically invalidated */
   139  	}
   140  	cgroupv1Features := []string{
   141  		"freezer-cgroup-v1", /* Snapd creates a freezer cgroup (v1) for each snap */
   142  	}
   143  
   144  	if cgroup.IsUnified() {
   145  		// TODO: update we get feature parity on cgroup v2
   146  		return commonFeatures
   147  	}
   148  
   149  	features := append(commonFeatures, cgroupv1Features...)
   150  	return features
   151  }