1package file 2 3import ( 4 "context" 5 "io/ioutil" 6 "log" 7 "os" 8 "path/filepath" 9 "strings" 10 "time" 11 12 "github.com/containerd/continuity/fs" 13 "github.com/docker/docker/pkg/idtools" 14 "github.com/moby/buildkit/snapshot" 15 "github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes" 16 "github.com/moby/buildkit/solver/pb" 17 "github.com/pkg/errors" 18 copy "github.com/tonistiigi/fsutil/copy" 19) 20 21func timestampToTime(ts int64) *time.Time { 22 if ts == -1 { 23 return nil 24 } 25 tm := time.Unix(ts/1e9, ts%1e9) 26 return &tm 27} 28 29func mapUserToChowner(user *copy.User, idmap *idtools.IdentityMapping) (copy.Chowner, error) { 30 if user == nil { 31 return func(old *copy.User) (*copy.User, error) { 32 if old == nil { 33 if idmap == nil { 34 return nil, nil 35 } 36 old = ©.User{} // root 37 // non-nil old is already mapped 38 if idmap != nil { 39 identity, err := idmap.ToHost(idtools.Identity{ 40 UID: old.UID, 41 GID: old.GID, 42 }) 43 if err != nil { 44 return nil, err 45 } 46 return ©.User{UID: identity.UID, GID: identity.GID}, nil 47 } 48 } 49 return old, nil 50 }, nil 51 } 52 u := *user 53 if idmap != nil { 54 identity, err := idmap.ToHost(idtools.Identity{ 55 UID: user.UID, 56 GID: user.GID, 57 }) 58 if err != nil { 59 return nil, err 60 } 61 u.UID = identity.UID 62 u.GID = identity.GID 63 } 64 return func(*copy.User) (*copy.User, error) { 65 return &u, nil 66 }, nil 67} 68 69func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy.User, idmap *idtools.IdentityMapping) error { 70 p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path))) 71 if err != nil { 72 return err 73 } 74 75 ch, err := mapUserToChowner(user, idmap) 76 if err != nil { 77 return err 78 } 79 80 if action.MakeParents { 81 if err := copy.MkdirAll(p, os.FileMode(action.Mode)&0777, ch, timestampToTime(action.Timestamp)); err != nil { 82 return err 83 } 84 } else { 85 if err := os.Mkdir(p, os.FileMode(action.Mode)&0777); err != nil { 86 if errors.Is(err, os.ErrExist) { 87 return nil 88 } 89 return err 90 } 91 if err := copy.Chown(p, nil, ch); err != nil { 92 return err 93 } 94 if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil { 95 return err 96 } 97 } 98 99 return nil 100} 101 102func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *copy.User, idmap *idtools.IdentityMapping) error { 103 p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path))) 104 if err != nil { 105 return err 106 } 107 108 ch, err := mapUserToChowner(user, idmap) 109 if err != nil { 110 return err 111 } 112 113 if err := ioutil.WriteFile(p, action.Data, os.FileMode(action.Mode)&0777); err != nil { 114 return err 115 } 116 117 if err := copy.Chown(p, nil, ch); err != nil { 118 return err 119 } 120 121 if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil { 122 return err 123 } 124 125 return nil 126} 127 128func rm(ctx context.Context, d string, action pb.FileActionRm) error { 129 if action.AllowWildcard { 130 src := cleanPath(action.Path) 131 m, err := copy.ResolveWildcards(d, src, false) 132 if err != nil { 133 return err 134 } 135 136 for _, s := range m { 137 if err := rmPath(d, s, action.AllowNotFound); err != nil { 138 return err 139 } 140 } 141 142 return nil 143 } 144 145 return rmPath(d, action.Path, action.AllowNotFound) 146} 147 148func rmPath(root, src string, allowNotFound bool) error { 149 p, err := fs.RootPath(root, filepath.Join(filepath.Join("/", src))) 150 if err != nil { 151 return err 152 } 153 154 if err := os.RemoveAll(p); err != nil { 155 if errors.Is(err, os.ErrNotExist) && allowNotFound { 156 return nil 157 } 158 return err 159 } 160 161 return nil 162} 163 164func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *copy.User, idmap *idtools.IdentityMapping) error { 165 srcPath := cleanPath(action.Src) 166 destPath := cleanPath(action.Dest) 167 168 if !action.CreateDestPath { 169 p, err := fs.RootPath(dest, filepath.Join(filepath.Join("/", action.Dest))) 170 if err != nil { 171 return err 172 } 173 if _, err := os.Lstat(filepath.Dir(p)); err != nil { 174 return errors.Wrapf(err, "failed to stat %s", action.Dest) 175 } 176 } 177 178 xattrErrorHandler := func(dst, src, key string, err error) error { 179 log.Println(err) 180 return nil 181 } 182 183 ch, err := mapUserToChowner(u, idmap) 184 if err != nil { 185 return err 186 } 187 188 opt := []copy.Opt{ 189 func(ci *copy.CopyInfo) { 190 ci.Chown = ch 191 ci.Utime = timestampToTime(action.Timestamp) 192 if m := int(action.Mode); m != -1 { 193 ci.Mode = &m 194 } 195 ci.CopyDirContents = action.DirCopyContents 196 ci.FollowLinks = action.FollowSymlink 197 }, 198 copy.WithXAttrErrorHandler(xattrErrorHandler), 199 } 200 201 if !action.AllowWildcard { 202 if action.AttemptUnpackDockerCompatibility { 203 if ok, err := unpack(ctx, src, srcPath, dest, destPath, ch, timestampToTime(action.Timestamp)); err != nil { 204 return err 205 } else if ok { 206 return nil 207 } 208 } 209 return copy.Copy(ctx, src, srcPath, dest, destPath, opt...) 210 } 211 212 m, err := copy.ResolveWildcards(src, srcPath, action.FollowSymlink) 213 if err != nil { 214 return err 215 } 216 217 if len(m) == 0 { 218 if action.AllowEmptyWildcard { 219 return nil 220 } 221 return errors.Errorf("%s not found", srcPath) 222 } 223 224 for _, s := range m { 225 if action.AttemptUnpackDockerCompatibility { 226 if ok, err := unpack(ctx, src, s, dest, destPath, ch, timestampToTime(action.Timestamp)); err != nil { 227 return err 228 } else if ok { 229 continue 230 } 231 } 232 if err := copy.Copy(ctx, src, s, dest, destPath, opt...); err != nil { 233 return err 234 } 235 } 236 237 return nil 238} 239 240func cleanPath(s string) string { 241 s2 := filepath.Join("/", s) 242 if strings.HasSuffix(s, "/.") { 243 if s2 != "/" { 244 s2 += "/" 245 } 246 s2 += "." 247 } else if strings.HasSuffix(s, "/") && s2 != "/" { 248 s2 += "/" 249 } 250 return s2 251} 252 253type Backend struct { 254} 255 256func (fb *Backend) Mkdir(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkDir) error { 257 mnt, ok := m.(*Mount) 258 if !ok { 259 return errors.Errorf("invalid mount type %T", m) 260 } 261 262 lm := snapshot.LocalMounter(mnt.m) 263 dir, err := lm.Mount() 264 if err != nil { 265 return err 266 } 267 defer lm.Unmount() 268 269 u, err := readUser(action.Owner, user, group) 270 if err != nil { 271 return err 272 } 273 274 return mkdir(ctx, dir, action, u, mnt.m.IdentityMapping()) 275} 276 277func (fb *Backend) Mkfile(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkFile) error { 278 mnt, ok := m.(*Mount) 279 if !ok { 280 return errors.Errorf("invalid mount type %T", m) 281 } 282 283 lm := snapshot.LocalMounter(mnt.m) 284 dir, err := lm.Mount() 285 if err != nil { 286 return err 287 } 288 defer lm.Unmount() 289 290 u, err := readUser(action.Owner, user, group) 291 if err != nil { 292 return err 293 } 294 295 return mkfile(ctx, dir, action, u, mnt.m.IdentityMapping()) 296} 297 298func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileActionRm) error { 299 mnt, ok := m.(*Mount) 300 if !ok { 301 return errors.Errorf("invalid mount type %T", m) 302 } 303 304 lm := snapshot.LocalMounter(mnt.m) 305 dir, err := lm.Mount() 306 if err != nil { 307 return err 308 } 309 defer lm.Unmount() 310 311 return rm(ctx, dir, action) 312} 313 314func (fb *Backend) Copy(ctx context.Context, m1, m2, user, group fileoptypes.Mount, action pb.FileActionCopy) error { 315 mnt1, ok := m1.(*Mount) 316 if !ok { 317 return errors.Errorf("invalid mount type %T", m1) 318 } 319 mnt2, ok := m2.(*Mount) 320 if !ok { 321 return errors.Errorf("invalid mount type %T", m2) 322 } 323 324 lm := snapshot.LocalMounter(mnt1.m) 325 src, err := lm.Mount() 326 if err != nil { 327 return err 328 } 329 defer lm.Unmount() 330 331 lm2 := snapshot.LocalMounter(mnt2.m) 332 dest, err := lm2.Mount() 333 if err != nil { 334 return err 335 } 336 defer lm2.Unmount() 337 338 u, err := readUser(action.Owner, user, group) 339 if err != nil { 340 return err 341 } 342 343 return docopy(ctx, src, dest, action, u, mnt2.m.IdentityMapping()) 344} 345