github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/cmd/snap-preseed/reset.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 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 main
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  
    28  	"github.com/snapcore/snapd/dirs"
    29  	"github.com/snapcore/snapd/osutil"
    30  	apparmor_sandbox "github.com/snapcore/snapd/sandbox/apparmor"
    31  )
    32  
    33  func resetPreseededChroot(preseedChroot string) error {
    34  	exists, isDir, err := osutil.DirExists(preseedChroot)
    35  	if err != nil {
    36  		return fmt.Errorf("cannot reset %q: %v", preseedChroot, err)
    37  	}
    38  	if !exists {
    39  		return fmt.Errorf("cannot reset non-existing directory %q", preseedChroot)
    40  	}
    41  	if !isDir {
    42  		return fmt.Errorf("cannot reset %q, it is not a directory", preseedChroot)
    43  	}
    44  
    45  	// globs that yield individual files
    46  	globs := []string{
    47  		dirs.SnapStateFile,
    48  		dirs.SnapSystemKeyFile,
    49  		filepath.Join(dirs.SnapBlobDir, "*.snap"),
    50  		filepath.Join(dirs.SnapUdevRulesDir, "*-snap.*.rules"),
    51  		filepath.Join(dirs.SnapDBusSystemPolicyDir, "snap.*.*.conf"),
    52  		filepath.Join(dirs.SnapServicesDir, "snap.*.service"),
    53  		filepath.Join(dirs.SnapServicesDir, "snap.*.timer"),
    54  		filepath.Join(dirs.SnapServicesDir, "snap.*.socket"),
    55  		filepath.Join(dirs.SnapServicesDir, "snap-*.mount"),
    56  		filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", "snap-*.mount"),
    57  		filepath.Join(dirs.SnapUserServicesDir, "snap.*.service"),
    58  		filepath.Join(dirs.SnapUserServicesDir, "snap.*.socket"),
    59  		filepath.Join(dirs.SnapUserServicesDir, "snap.*.timer"),
    60  		filepath.Join(dirs.SnapUserServicesDir, "default.target.wants", "snap.*.service"),
    61  		filepath.Join(dirs.SnapUserServicesDir, "sockets.target.wants", "snap.*.socket"),
    62  		filepath.Join(dirs.SnapUserServicesDir, "timers.target.wants", "snap.*.timer"),
    63  	}
    64  
    65  	for _, gl := range globs {
    66  		matches, err := filepath.Glob(filepath.Join(preseedChroot, gl))
    67  		if err != nil {
    68  			// the only possible error from Glob() is ErrBadPattern
    69  			return err
    70  		}
    71  		for _, path := range matches {
    72  			if err := os.Remove(path); err != nil {
    73  				return fmt.Errorf("error removing %s: %v", path, err)
    74  			}
    75  		}
    76  	}
    77  
    78  	// directories that need to be removed recursively (but
    79  	// leaving parent directory intact).
    80  	globs = []string{
    81  		filepath.Join(dirs.SnapDataDir, "*"),
    82  		filepath.Join(dirs.SnapCacheDir, "*"),
    83  		filepath.Join(apparmor_sandbox.CacheDir, "*"),
    84  		filepath.Join(dirs.SnapDesktopFilesDir, "*"),
    85  	}
    86  
    87  	for _, gl := range globs {
    88  		matches, err := filepath.Glob(filepath.Join(preseedChroot, gl))
    89  		if err != nil {
    90  			// the only possible error from Glob() is ErrBadPattern
    91  			return err
    92  		}
    93  		for _, path := range matches {
    94  			if err := os.RemoveAll(path); err != nil {
    95  				return fmt.Errorf("error removing %s: %v", path, err)
    96  			}
    97  		}
    98  	}
    99  
   100  	// directories removed entirely
   101  	paths := []string{
   102  		dirs.SnapAssertsDBDir,
   103  		dirs.FeaturesDir,
   104  		dirs.SnapDesktopIconsDir,
   105  		dirs.SnapDeviceDir,
   106  		dirs.SnapCookieDir,
   107  		dirs.SnapMountPolicyDir,
   108  		dirs.SnapAppArmorDir,
   109  		dirs.SnapSeqDir,
   110  		dirs.SnapMountDir,
   111  		dirs.SnapSeccompBase,
   112  	}
   113  
   114  	for _, path := range paths {
   115  		if err := os.RemoveAll(filepath.Join(preseedChroot, path)); err != nil {
   116  			// report the error and carry on
   117  			return fmt.Errorf("error removing %s: %v", path, err)
   118  		}
   119  	}
   120  
   121  	// bash-completion symlinks; note there are symlinks that point at
   122  	// completer, and symlinks that point at the completer symlinks.
   123  	// e.g.
   124  	// lxd.lxc -> /snap/core/current/usr/lib/snapd/complete.sh
   125  	// lxc -> lxd.lxc
   126  	files, err := ioutil.ReadDir(filepath.Join(preseedChroot, dirs.CompletersDir))
   127  	if err != nil && !os.IsNotExist(err) {
   128  		return fmt.Errorf("error reading %s: %v", dirs.CompletersDir, err)
   129  	}
   130  	completeShSymlinks := make(map[string]string)
   131  	var otherSymlinks []string
   132  
   133  	// pass 1: find all symlinks pointing at complete.sh
   134  	for _, fileInfo := range files {
   135  		if fileInfo.Mode()&os.ModeSymlink == 0 {
   136  			continue
   137  		}
   138  		fullPath := filepath.Join(preseedChroot, dirs.CompletersDir, fileInfo.Name())
   139  		if dirs.IsCompleteShSymlink(fullPath) {
   140  			if err := os.Remove(fullPath); err != nil {
   141  				return fmt.Errorf("error removing symlink %s: %v", fullPath, err)
   142  			}
   143  			completeShSymlinks[fileInfo.Name()] = fullPath
   144  		} else {
   145  			otherSymlinks = append(otherSymlinks, fullPath)
   146  		}
   147  	}
   148  	// pass 2: find all symlinks that point at the symlinks found in pass 1.
   149  	for _, other := range otherSymlinks {
   150  		target, err := os.Readlink(other)
   151  		if err != nil {
   152  			return fmt.Errorf("error reading symlink target of %s: %v", other, err)
   153  		}
   154  		if _, ok := completeShSymlinks[target]; ok {
   155  			if err := os.Remove(other); err != nil {
   156  				return fmt.Errorf("error removing symlink %s: %v", other, err)
   157  			}
   158  		}
   159  	}
   160  
   161  	return nil
   162  }