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 }