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