github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/overlay_linux.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2018 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 osutil
    21  
    22  import (
    23  	"fmt"
    24  	"strings"
    25  )
    26  
    27  // IsRootWritableOverlay detects if the current '/' is a writable overlay
    28  // (fstype is 'overlay' and 'upperdir' is specified) and returns upperdir or
    29  // the empty string if not used.
    30  //
    31  // Debian-based LiveCD systems use 'casper' to setup the mounts, and part of
    32  // this setup involves running mount commands to mount / on /cow as overlay and
    33  // results in AppArmor seeing '/upper' as the upperdir rather than '/cow/upper'
    34  // as seen in mountinfo. By the time snapd is run, we don't have enough
    35  // information to discover /cow through mount parent ID or st_dev (maj:min).
    36  // While overlay doesn't use the mount source for anything itself, casper sets
    37  // the mount source ('/cow' with the above) for its own purposes and we can
    38  // leverage this by stripping the mount source from the beginning of upperdir.
    39  //
    40  // https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt
    41  // man 5 proc
    42  //
    43  // Currently uses variables and Mock functions from nfs.go
    44  func IsRootWritableOverlay() (string, error) {
    45  	mountinfo, err := LoadMountInfo()
    46  	if err != nil {
    47  		return "", fmt.Errorf("cannot parse mountinfo: %s", err)
    48  	}
    49  	for _, entry := range mountinfo {
    50  		if entry.FsType == "overlay" && entry.MountDir == "/" {
    51  			if dir, ok := entry.SuperOptions["upperdir"]; ok {
    52  				// upperdir must be an absolute path without
    53  				// any AppArmor regular expression (AARE)
    54  				// characters or double quotes to be considered
    55  				if !strings.HasPrefix(dir, "/") || strings.ContainsAny(dir, `?*[]{}^"`) {
    56  					continue
    57  				}
    58  				// if mount source is path, strip it from dir
    59  				// (for casper)
    60  				if strings.HasPrefix(entry.MountSource, "/") {
    61  					dir = strings.TrimPrefix(dir, strings.TrimRight(entry.MountSource, "/"))
    62  				}
    63  
    64  				dir = strings.TrimRight(dir, "/")
    65  
    66  				// The resulting trimmed dir must be an
    67  				// absolute path that is not '/'
    68  				if len(dir) < 2 || !strings.HasPrefix(dir, "/") {
    69  					continue
    70  				}
    71  
    72  				switch dir {
    73  				case "/media/root-rw/overlay":
    74  					// On the Ubuntu server ephemeral image, '/' is setup via
    75  					// overlayroot (on at least 18.10), which uses a combination
    76  					// of overlayfs and chroot. This differs from the livecd setup
    77  					// so special case the detection logic to look for the known
    78  					// upperdir for this configuration, and return the required
    79  					// path. See LP: #1797218 for details.
    80  					return "/overlay", nil
    81  				case "/run/miso/overlay_root/upper":
    82  					// On the Manjaro ephemeral image, '/' is setup via
    83  					// overlayroot. This is similar to the workaround above.
    84  					return "/upper", nil
    85  				}
    86  
    87  				// Make sure trailing slashes are predictably missing
    88  				return dir, nil
    89  			}
    90  		}
    91  	}
    92  	return "", nil
    93  }