github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/snap/snapdir/snapdir.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 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 snapdir 21 22 import ( 23 "fmt" 24 "io" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 29 "github.com/snapcore/snapd/osutil" 30 "github.com/snapcore/snapd/snap" 31 "github.com/snapcore/snapd/snap/internal" 32 ) 33 34 func IsSnapDir(path string) bool { 35 if osutil.IsDirectory(path) { 36 if osutil.FileExists(filepath.Join(path, "meta", "snap.yaml")) { 37 return true 38 } 39 } 40 return false 41 } 42 43 // SnapDir is the snapdir based snap. 44 type SnapDir struct { 45 path string 46 } 47 48 // Path returns the path of the backing container. 49 func (s *SnapDir) Path() string { 50 return s.path 51 } 52 53 // New returns a new snap directory container. 54 func New(path string) *SnapDir { 55 return &SnapDir{path: path} 56 } 57 58 func (s *SnapDir) Size() (size int64, err error) { 59 totalSize := int64(0) 60 f := func(_ string, info os.FileInfo, err error) error { 61 totalSize += info.Size() 62 return err 63 } 64 filepath.Walk(s.path, f) 65 66 return totalSize, nil 67 } 68 69 func (s *SnapDir) Install(targetPath, mountDir string, opts *snap.InstallOptions) (bool, error) { 70 // TODO:UC20: support MustNotCrossDevices somehow here 71 72 return false, os.Symlink(s.path, targetPath) 73 } 74 75 func (s *SnapDir) RandomAccessFile(file string) (interface { 76 io.ReaderAt 77 io.Closer 78 Size() int64 79 }, error) { 80 f, err := os.Open(filepath.Join(s.path, file)) 81 if err != nil { 82 return nil, err 83 } 84 return internal.NewSizedFile(f) 85 } 86 87 func (s *SnapDir) ReadFile(file string) (content []byte, err error) { 88 return ioutil.ReadFile(filepath.Join(s.path, file)) 89 } 90 91 func littleWalk(dirPath string, dirHandle *os.File, dirstack *[]string, walkFn filepath.WalkFunc) error { 92 const numSt = 100 93 94 sts, err := dirHandle.Readdir(numSt) 95 if err != nil { 96 return err 97 } 98 for _, st := range sts { 99 path := filepath.Join(dirPath, st.Name()) 100 if err := walkFn(path, st, nil); err != nil { 101 if st.IsDir() && err == filepath.SkipDir { 102 // caller wants to skip this directory 103 continue 104 } 105 return err 106 } else if st.IsDir() { 107 *dirstack = append(*dirstack, path) 108 } 109 } 110 111 return nil 112 } 113 114 // Walk (part of snap.Container) is like filepath.Walk, without the ordering guarantee. 115 func (s *SnapDir) Walk(relative string, walkFn filepath.WalkFunc) error { 116 relative = filepath.Clean(relative) 117 if relative == "" || relative == "/" { 118 relative = "." 119 } else if relative[0] == '/' { 120 // I said relative, darn it :-) 121 relative = relative[1:] 122 } 123 root := filepath.Join(s.path, relative) 124 // we could just filepath.Walk(root, walkFn), but that doesn't scale 125 // well to insanely big directories as it reads the whole directory, 126 // in order to sort it. This Walk doesn't do that. 127 // 128 // Also the directory is always relative to the top of the container 129 // for us, which would make it a little more messy to get right. 130 f, err := os.Open(root) 131 if err != nil { 132 return walkFn(relative, nil, err) 133 } 134 defer func() { 135 if f != nil { 136 f.Close() 137 } 138 }() 139 140 st, err := f.Stat() 141 if err != nil { 142 return walkFn(relative, nil, err) 143 } 144 145 err = walkFn(relative, st, nil) 146 if err != nil { 147 return err 148 } 149 if !st.IsDir() { 150 return nil 151 } 152 153 var dirstack []string 154 for { 155 if err := littleWalk(relative, f, &dirstack, walkFn); err != nil { 156 if err != io.EOF { 157 err = walkFn(relative, nil, err) 158 if err != nil { 159 return err 160 } 161 } 162 if len(dirstack) == 0 { 163 // finished 164 break 165 } 166 f.Close() 167 f = nil 168 for f == nil && len(dirstack) > 0 { 169 relative = dirstack[0] 170 f, err = os.Open(filepath.Join(s.path, relative)) 171 if err != nil { 172 err = walkFn(relative, nil, err) 173 if err != nil { 174 return err 175 } 176 } 177 dirstack = dirstack[1:] 178 } 179 if f == nil { 180 break 181 } 182 continue 183 } 184 } 185 186 return nil 187 } 188 189 func (s *SnapDir) ListDir(path string) ([]string, error) { 190 fileInfos, err := ioutil.ReadDir(filepath.Join(s.path, path)) 191 if err != nil { 192 return nil, err 193 } 194 195 var fileNames []string 196 for _, fileInfo := range fileInfos { 197 fileNames = append(fileNames, fileInfo.Name()) 198 } 199 200 return fileNames, nil 201 } 202 203 func (s *SnapDir) Unpack(src, dstDir string) error { 204 return fmt.Errorf("unpack is not supported with snaps of type snapdir") 205 }