...

Source file src/github.com/Microsoft/hcsshim/internal/guest/storage/pmem/pmem.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/storage/pmem

     1  //go:build linux
     2  // +build linux
     3  
     4  package pmem
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os"
    10  
    11  	"github.com/pkg/errors"
    12  	"go.opencensus.io/trace"
    13  	"golang.org/x/sys/unix"
    14  
    15  	"github.com/Microsoft/hcsshim/internal/guest/storage"
    16  	dm "github.com/Microsoft/hcsshim/internal/guest/storage/devicemapper"
    17  	"github.com/Microsoft/hcsshim/internal/log"
    18  	"github.com/Microsoft/hcsshim/internal/oc"
    19  	"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
    20  )
    21  
    22  // Test dependencies
    23  var (
    24  	osMkdirAll                   = os.MkdirAll
    25  	osRemoveAll                  = os.RemoveAll
    26  	unixMount                    = unix.Mount
    27  	mountInternal                = mount
    28  	createZeroSectorLinearTarget = dm.CreateZeroSectorLinearTarget
    29  	createVerityTarget           = dm.CreateVerityTarget
    30  	removeDevice                 = dm.RemoveDevice
    31  )
    32  
    33  const (
    34  	pMemFmt         = "/dev/pmem%d"
    35  	linearDeviceFmt = "dm-linear-pmem%d-%d-%d"
    36  	verityDeviceFmt = "dm-verity-pmem%d-%s"
    37  )
    38  
    39  // mount mounts source to target via unix.Mount
    40  func mount(ctx context.Context, source, target string) (err error) {
    41  	if err := osMkdirAll(target, 0700); err != nil {
    42  		return err
    43  	}
    44  	defer func() {
    45  		if err != nil {
    46  			if err := osRemoveAll(target); err != nil {
    47  				log.G(ctx).WithError(err).Debugf("error cleaning up target: %s", target)
    48  			}
    49  		}
    50  	}()
    51  
    52  	flags := uintptr(unix.MS_RDONLY)
    53  	if err := unixMount(source, target, "ext4", flags, "noload"); err != nil {
    54  		return errors.Wrapf(err, "failed to mount %s onto %s", source, target)
    55  	}
    56  	return nil
    57  }
    58  
    59  // Mount mounts the pmem device at `/dev/pmem<device>` to `target` in a basic scenario.
    60  // If either mappingInfo or verityInfo are non-nil, the device-mapper framework is used
    61  // to create linear and verity targets accordingly. If both are non-nil, the linear
    62  // target is created first and used as the data/hash device for the verity target.
    63  //
    64  // `target` will be created. On mount failure the created `target` will be
    65  // automatically cleaned up.
    66  //
    67  // Note: For now the platform only supports readonly pmem that is assumed to be
    68  // `ext4`.
    69  //
    70  // Note: both mappingInfo and verityInfo can be non-nil at the same time, in that case
    71  // linear target is created first and it becomes the data/hash device for verity target.
    72  func Mount(
    73  	ctx context.Context,
    74  	device uint32,
    75  	target string,
    76  	mappingInfo *guestresource.LCOWVPMemMappingInfo,
    77  	verityInfo *guestresource.DeviceVerityInfo,
    78  ) (err error) {
    79  	mCtx, span := oc.StartSpan(ctx, "pmem::Mount")
    80  	defer span.End()
    81  	defer func() { oc.SetSpanStatus(span, err) }()
    82  
    83  	span.AddAttributes(
    84  		trace.Int64Attribute("deviceNumber", int64(device)),
    85  		trace.StringAttribute("target", target))
    86  
    87  	devicePath := fmt.Sprintf(pMemFmt, device)
    88  
    89  	// dm-linear target has to be created first. When verity info is also present, the linear target becomes the data
    90  	// device instead of the original VPMem.
    91  	if mappingInfo != nil {
    92  		dmLinearName := fmt.Sprintf(linearDeviceFmt, device, mappingInfo.DeviceOffsetInBytes, mappingInfo.DeviceSizeInBytes)
    93  		if devicePath, err = createZeroSectorLinearTarget(mCtx, devicePath, dmLinearName, mappingInfo); err != nil {
    94  			return err
    95  		}
    96  		defer func() {
    97  			if err != nil {
    98  				if err := removeDevice(dmLinearName); err != nil {
    99  					log.G(mCtx).WithError(err).Debugf("failed to cleanup linear target: %s", dmLinearName)
   100  				}
   101  			}
   102  		}()
   103  	}
   104  
   105  	if verityInfo != nil {
   106  		dmVerityName := fmt.Sprintf(verityDeviceFmt, device, verityInfo.RootDigest)
   107  		if devicePath, err = createVerityTarget(mCtx, devicePath, dmVerityName, verityInfo); err != nil {
   108  			return err
   109  		}
   110  		defer func() {
   111  			if err != nil {
   112  				if err := removeDevice(dmVerityName); err != nil {
   113  					log.G(mCtx).WithError(err).Debugf("failed to cleanup verity target: %s", dmVerityName)
   114  				}
   115  			}
   116  		}()
   117  	}
   118  
   119  	return mountInternal(mCtx, devicePath, target)
   120  }
   121  
   122  // Unmount unmounts `target` and removes corresponding linear and verity targets when needed
   123  func Unmount(
   124  	ctx context.Context,
   125  	devNumber uint32,
   126  	target string,
   127  	mappingInfo *guestresource.LCOWVPMemMappingInfo,
   128  	verityInfo *guestresource.DeviceVerityInfo,
   129  ) (err error) {
   130  	_, span := oc.StartSpan(ctx, "pmem::Unmount")
   131  	defer span.End()
   132  	defer func() { oc.SetSpanStatus(span, err) }()
   133  
   134  	span.AddAttributes(
   135  		trace.Int64Attribute("device", int64(devNumber)),
   136  		trace.StringAttribute("target", target))
   137  
   138  	if err := storage.UnmountPath(ctx, target, true); err != nil {
   139  		return errors.Wrapf(err, "failed to unmount target: %s", target)
   140  	}
   141  
   142  	if verityInfo != nil {
   143  		dmVerityName := fmt.Sprintf(verityDeviceFmt, devNumber, verityInfo.RootDigest)
   144  		if err := dm.RemoveDevice(dmVerityName); err != nil {
   145  			// The target is already unmounted at this point, ignore potential errors
   146  			log.G(ctx).WithError(err).Debugf("failed to remove dm verity target: %s", dmVerityName)
   147  		}
   148  	}
   149  
   150  	if mappingInfo != nil {
   151  		dmLinearName := fmt.Sprintf(linearDeviceFmt, devNumber, mappingInfo.DeviceOffsetInBytes, mappingInfo.DeviceSizeInBytes)
   152  		if err := dm.RemoveDevice(dmLinearName); err != nil {
   153  			// The target is already unmounted at this point, ignore potential errors
   154  			log.G(ctx).WithError(err).Debugf("failed to remove dm linear target: %s", dmLinearName)
   155  		}
   156  	}
   157  
   158  	return nil
   159  }
   160  

View as plain text