github.com/containerd/Containerd@v1.4.13/snapshots/native/native.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package native 18 19 import ( 20 "context" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 25 "github.com/containerd/containerd/log" 26 "github.com/containerd/containerd/mount" 27 "github.com/containerd/containerd/platforms" 28 "github.com/containerd/containerd/plugin" 29 "github.com/containerd/containerd/snapshots" 30 "github.com/containerd/containerd/snapshots/storage" 31 32 "github.com/containerd/continuity/fs" 33 "github.com/pkg/errors" 34 ) 35 36 func init() { 37 plugin.Register(&plugin.Registration{ 38 Type: plugin.SnapshotPlugin, 39 ID: "native", 40 InitFn: func(ic *plugin.InitContext) (interface{}, error) { 41 ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec()) 42 return NewSnapshotter(ic.Root) 43 }, 44 }) 45 } 46 47 type snapshotter struct { 48 root string 49 ms *storage.MetaStore 50 } 51 52 // NewSnapshotter returns a Snapshotter which copies layers on the underlying 53 // file system. A metadata file is stored under the root. 54 func NewSnapshotter(root string) (snapshots.Snapshotter, error) { 55 if err := os.MkdirAll(root, 0700); err != nil { 56 return nil, err 57 } 58 ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db")) 59 if err != nil { 60 return nil, err 61 } 62 63 if err := os.Mkdir(filepath.Join(root, "snapshots"), 0700); err != nil && !os.IsExist(err) { 64 return nil, err 65 } 66 67 return &snapshotter{ 68 root: root, 69 ms: ms, 70 }, nil 71 } 72 73 // Stat returns the info for an active or committed snapshot by name or 74 // key. 75 // 76 // Should be used for parent resolution, existence checks and to discern 77 // the kind of snapshot. 78 func (o *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) { 79 ctx, t, err := o.ms.TransactionContext(ctx, false) 80 if err != nil { 81 return snapshots.Info{}, err 82 } 83 defer t.Rollback() 84 _, info, _, err := storage.GetInfo(ctx, key) 85 if err != nil { 86 return snapshots.Info{}, err 87 } 88 89 return info, nil 90 } 91 92 func (o *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { 93 ctx, t, err := o.ms.TransactionContext(ctx, true) 94 if err != nil { 95 return snapshots.Info{}, err 96 } 97 98 info, err = storage.UpdateInfo(ctx, info, fieldpaths...) 99 if err != nil { 100 t.Rollback() 101 return snapshots.Info{}, err 102 } 103 104 if err := t.Commit(); err != nil { 105 return snapshots.Info{}, err 106 } 107 108 return info, nil 109 } 110 111 func (o *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) { 112 ctx, t, err := o.ms.TransactionContext(ctx, false) 113 if err != nil { 114 return snapshots.Usage{}, err 115 } 116 defer t.Rollback() 117 118 id, info, usage, err := storage.GetInfo(ctx, key) 119 if err != nil { 120 return snapshots.Usage{}, err 121 } 122 123 if info.Kind == snapshots.KindActive { 124 du, err := fs.DiskUsage(ctx, o.getSnapshotDir(id)) 125 if err != nil { 126 return snapshots.Usage{}, err 127 } 128 usage = snapshots.Usage(du) 129 } 130 131 return usage, nil 132 } 133 134 func (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { 135 return o.createSnapshot(ctx, snapshots.KindActive, key, parent, opts) 136 } 137 138 func (o *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { 139 return o.createSnapshot(ctx, snapshots.KindView, key, parent, opts) 140 } 141 142 // Mounts returns the mounts for the transaction identified by key. Can be 143 // called on an read-write or readonly transaction. 144 // 145 // This can be used to recover mounts after calling View or Prepare. 146 func (o *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) { 147 ctx, t, err := o.ms.TransactionContext(ctx, false) 148 if err != nil { 149 return nil, err 150 } 151 s, err := storage.GetSnapshot(ctx, key) 152 t.Rollback() 153 if err != nil { 154 return nil, errors.Wrap(err, "failed to get snapshot mount") 155 } 156 return o.mounts(s), nil 157 } 158 159 func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error { 160 ctx, t, err := o.ms.TransactionContext(ctx, true) 161 if err != nil { 162 return err 163 } 164 165 id, _, _, err := storage.GetInfo(ctx, key) 166 if err != nil { 167 return err 168 } 169 170 usage, err := fs.DiskUsage(ctx, o.getSnapshotDir(id)) 171 if err != nil { 172 return err 173 } 174 175 if _, err := storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil { 176 if rerr := t.Rollback(); rerr != nil { 177 log.G(ctx).WithError(rerr).Warn("failed to rollback transaction") 178 } 179 return errors.Wrap(err, "failed to commit snapshot") 180 } 181 return t.Commit() 182 } 183 184 // Remove abandons the transaction identified by key. All resources 185 // associated with the key will be removed. 186 func (o *snapshotter) Remove(ctx context.Context, key string) (err error) { 187 ctx, t, err := o.ms.TransactionContext(ctx, true) 188 if err != nil { 189 return err 190 } 191 defer func() { 192 if err != nil && t != nil { 193 if rerr := t.Rollback(); rerr != nil { 194 log.G(ctx).WithError(rerr).Warn("failed to rollback transaction") 195 } 196 } 197 }() 198 199 id, _, err := storage.Remove(ctx, key) 200 if err != nil { 201 return errors.Wrap(err, "failed to remove") 202 } 203 204 path := o.getSnapshotDir(id) 205 renamed := filepath.Join(o.root, "snapshots", "rm-"+id) 206 if err := os.Rename(path, renamed); err != nil { 207 if !os.IsNotExist(err) { 208 return errors.Wrap(err, "failed to rename") 209 } 210 renamed = "" 211 } 212 213 err = t.Commit() 214 t = nil 215 if err != nil { 216 if renamed != "" { 217 if err1 := os.Rename(renamed, path); err1 != nil { 218 // May cause inconsistent data on disk 219 log.G(ctx).WithError(err1).WithField("path", renamed).Errorf("failed to rename after failed commit") 220 } 221 } 222 return errors.Wrap(err, "failed to commit") 223 } 224 if renamed != "" { 225 if err := os.RemoveAll(renamed); err != nil { 226 // Must be cleaned up, any "rm-*" could be removed if no active transactions 227 log.G(ctx).WithError(err).WithField("path", renamed).Warnf("failed to remove root filesystem") 228 } 229 } 230 231 return nil 232 } 233 234 // Walk the committed snapshots. 235 func (o *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error { 236 ctx, t, err := o.ms.TransactionContext(ctx, false) 237 if err != nil { 238 return err 239 } 240 defer t.Rollback() 241 return storage.WalkInfo(ctx, fn, fs...) 242 } 243 244 func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) (_ []mount.Mount, err error) { 245 var ( 246 path, td string 247 ) 248 249 if kind == snapshots.KindActive || parent == "" { 250 td, err = ioutil.TempDir(filepath.Join(o.root, "snapshots"), "new-") 251 if err != nil { 252 return nil, errors.Wrap(err, "failed to create temp dir") 253 } 254 if err := os.Chmod(td, 0755); err != nil { 255 return nil, errors.Wrapf(err, "failed to chmod %s to 0755", td) 256 } 257 defer func() { 258 if err != nil { 259 if td != "" { 260 if err1 := os.RemoveAll(td); err1 != nil { 261 err = errors.Wrapf(err, "remove failed: %v", err1) 262 } 263 } 264 if path != "" { 265 if err1 := os.RemoveAll(path); err1 != nil { 266 err = errors.Wrapf(err, "failed to remove path: %v", err1) 267 } 268 } 269 } 270 }() 271 } 272 273 ctx, t, err := o.ms.TransactionContext(ctx, true) 274 if err != nil { 275 return nil, err 276 } 277 278 s, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...) 279 if err != nil { 280 if rerr := t.Rollback(); rerr != nil { 281 log.G(ctx).WithError(rerr).Warn("failed to rollback transaction") 282 } 283 return nil, errors.Wrap(err, "failed to create snapshot") 284 } 285 286 if td != "" { 287 if len(s.ParentIDs) > 0 { 288 parent := o.getSnapshotDir(s.ParentIDs[0]) 289 xattrErrorHandler := func(dst, src, xattrKey string, copyErr error) error { 290 // security.* xattr cannot be copied in most cases (moby/buildkit#1189) 291 log.G(ctx).WithError(copyErr).Debugf("failed to copy xattr %q", xattrKey) 292 return nil 293 } 294 copyDirOpts := []fs.CopyDirOpt{ 295 fs.WithXAttrErrorHandler(xattrErrorHandler), 296 } 297 if err := fs.CopyDir(td, parent, copyDirOpts...); err != nil { 298 return nil, errors.Wrap(err, "copying of parent failed") 299 } 300 } 301 302 path = o.getSnapshotDir(s.ID) 303 if err := os.Rename(td, path); err != nil { 304 if rerr := t.Rollback(); rerr != nil { 305 log.G(ctx).WithError(rerr).Warn("failed to rollback transaction") 306 } 307 return nil, errors.Wrap(err, "failed to rename") 308 } 309 td = "" 310 } 311 312 if err := t.Commit(); err != nil { 313 return nil, errors.Wrap(err, "commit failed") 314 } 315 316 return o.mounts(s), nil 317 } 318 319 func (o *snapshotter) getSnapshotDir(id string) string { 320 return filepath.Join(o.root, "snapshots", id) 321 } 322 323 func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount { 324 var ( 325 roFlag string 326 source string 327 ) 328 329 if s.Kind == snapshots.KindView { 330 roFlag = "ro" 331 } else { 332 roFlag = "rw" 333 } 334 335 if len(s.ParentIDs) == 0 || s.Kind == snapshots.KindActive { 336 source = o.getSnapshotDir(s.ID) 337 } else { 338 source = o.getSnapshotDir(s.ParentIDs[0]) 339 } 340 341 return []mount.Mount{ 342 { 343 Source: source, 344 Type: "bind", 345 Options: []string{ 346 roFlag, 347 "rbind", 348 }, 349 }, 350 } 351 } 352 353 // Close closes the snapshotter 354 func (o *snapshotter) Close() error { 355 return o.ms.Close() 356 }