@@ -22,11 +22,14 @@ import (
2222 "os"
2323 "path/filepath"
2424 "regexp"
25+ "sort"
26+ "strconv"
2527 "strings"
2628 "syscall"
2729 "unsafe"
2830
2931 "github.com/Microsoft/hcsshim"
32+ "github.com/pkg/errors"
3033 "golang.org/x/sys/windows"
3134)
3235
@@ -257,12 +260,71 @@ func windowsOpenSequential(path string, mode int, _ uint32) (fd windows.Handle,
257260 return h , e
258261}
259262
260- // ForceRemoveAll is the same as os.RemoveAll, but uses hcsshim.DestroyLayer in order
261- // to delete container layers.
263+ // ForceRemoveAll is the same as os.RemoveAll, but is aware of io.containerd.snapshotter.v1.windows
264+ // and uses hcsshim to unmount and delete container layers contained therein, in the correct order,
265+ // when passed a containerd root data directory (i.e. the `--root` directory for containerd).
262266func ForceRemoveAll (path string ) error {
267+ // snapshots/windows/windows.go init()
268+ const snapshotPlugin = "io.containerd.snapshotter.v1" + "." + "windows"
269+ // snapshots/windows/windows.go NewSnapshotter()
270+ snapshotDir := filepath .Join (path , snapshotPlugin , "snapshots" )
271+ if stat , err := os .Stat (snapshotDir ); err == nil && stat .IsDir () {
272+ if err := cleanupWCOWLayers (snapshotDir ); err != nil {
273+ return errors .Wrapf (err , "failed to cleanup WCOW layers in %s" , snapshotDir )
274+ }
275+ }
276+
277+ return os .RemoveAll (path )
278+ }
279+
280+ func cleanupWCOWLayers (root string ) error {
281+ // See snapshots/windows/windows.go getSnapshotDir()
282+ var layerNums []int
283+ if err := filepath .Walk (root , func (path string , info os.FileInfo , err error ) error {
284+ if path != root && info .IsDir () {
285+ if layerNum , err := strconv .Atoi (filepath .Base (path )); err == nil {
286+ layerNums = append (layerNums , layerNum )
287+ } else {
288+ return err
289+ }
290+ return filepath .SkipDir
291+ }
292+
293+ return nil
294+ }); err != nil {
295+ return err
296+ }
297+
298+ sort .Sort (sort .Reverse (sort .IntSlice (layerNums )))
299+
300+ for _ , layerNum := range layerNums {
301+ if err := cleanupWCOWLayer (filepath .Join (root , strconv .Itoa (layerNum ))); err != nil {
302+ return err
303+ }
304+ }
305+
306+ return nil
307+ }
308+
309+ func cleanupWCOWLayer (layerPath string ) error {
263310 info := hcsshim.DriverInfo {
264- HomeDir : filepath .Dir (path ),
311+ HomeDir : filepath .Dir (layerPath ),
265312 }
266313
267- return hcsshim .DestroyLayer (info , filepath .Base (path ))
314+ // ERROR_DEV_NOT_EXIST is returned if the layer is not currently prepared.
315+ if err := hcsshim .UnprepareLayer (info , filepath .Base (layerPath )); err != nil {
316+ if hcserror , ok := err .(* hcsshim.HcsError ); ! ok || hcserror .Err != windows .ERROR_DEV_NOT_EXIST {
317+ return errors .Wrapf (err , "failed to unprepare %s" , layerPath )
318+ }
319+ }
320+
321+ if err := hcsshim .DeactivateLayer (info , filepath .Base (layerPath )); err != nil {
322+ return errors .Wrapf (err , "failed to deactivate %s" , layerPath )
323+ }
324+
325+ if err := hcsshim .DestroyLayer (info , filepath .Base (layerPath )); err != nil {
326+ return errors .Wrapf (err , "failed to destroy %s" , layerPath )
327+ }
328+
329+ return nil
268330}
0 commit comments