Exemplo n.º 1
0
// Load mount table
func (m *DeviceMounter) Load(devPrefix string) error {
	info, err := mount.GetMounts()
	if err != nil {
		return err
	}
DeviceLoop:
	for _, v := range info {
		if !strings.HasPrefix(v.Source, devPrefix) {
			continue
		}
		mount, ok := m.mounts[v.Source]
		if !ok {
			mount = &Info{
				Device:     v.Source,
				Fs:         v.Fstype,
				Minor:      v.Minor,
				Mountpoint: make([]PathInfo, 0),
			}
			m.mounts[v.Source] = mount
		}
		// Allow Load to be called multiple times.
		for _, p := range mount.Mountpoint {
			if p.Path == v.Mountpoint {
				continue DeviceLoop
			}
		}
		// XXX Reconstruct refs.
		mount.Mountpoint = append(mount.Mountpoint, PathInfo{Path: v.Mountpoint, ref: 1})
		m.paths[v.Mountpoint] = v.Source
	}
	return nil
}
Exemplo n.º 2
0
func (clnt *client) setExited(containerID string) error {
	clnt.lock(containerID)
	defer clnt.unlock(containerID)

	var exitCode uint32
	if event, ok := clnt.remote.pastEvents[containerID]; ok {
		exitCode = event.Status
		delete(clnt.remote.pastEvents, containerID)
	}

	err := clnt.backend.StateChanged(containerID, StateInfo{
		State:    StateExit,
		ExitCode: exitCode,
	})

	// Unmount and delete the bundle folder
	if mts, err := mount.GetMounts(); err == nil {
		for _, mts := range mts {
			if strings.HasSuffix(mts.Mountpoint, containerID+"/rootfs") {
				if err := syscall.Unmount(mts.Mountpoint, syscall.MNT_DETACH); err == nil {
					os.RemoveAll(strings.TrimSuffix(mts.Mountpoint, "/rootfs"))
				}
				break
			}
		}
	}

	return err
}
Exemplo n.º 3
0
// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
// filesystem or an empty string if no mountpoint is found.  Selinuxfs is
// a proc-like pseudo-filesystem that exposes the selinux policy API to
// processes.  The existence of an selinuxfs mount is used to determine
// whether selinux is currently enabled or not.
func getSelinuxMountPoint() string {
	if selinuxfs != "unknown" {
		return selinuxfs
	}
	selinuxfs = ""

	mounts, err := mount.GetMounts()
	if err != nil {
		return selinuxfs
	}
	for _, mount := range mounts {
		if mount.Fstype == "selinuxfs" {
			selinuxfs = mount.Mountpoint
			break
		}
	}
	if selinuxfs != "" {
		var buf syscall.Statfs_t
		syscall.Statfs(selinuxfs, &buf)
		if (buf.Flags & stRdOnly) == 1 {
			selinuxfs = ""
		}
	}
	return selinuxfs
}
Exemplo n.º 4
0
func GetCgroupMounts() ([]Mount, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}

	all, err := GetAllSubsystems()
	if err != nil {
		return nil, err
	}

	allMap := make(map[string]bool)
	for _, s := range all {
		allMap[s] = true
	}

	res := []Mount{}
	for _, mount := range mounts {
		if mount.Fstype == "cgroup" {
			m := Mount{Mountpoint: mount.Mountpoint}

			for _, opt := range strings.Split(mount.VfsOpts, ",") {
				if strings.HasPrefix(opt, cgroupNamePrefix) {
					m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
				}
				if allMap[opt] {
					m.Subsystems = append(m.Subsystems, opt)
				}
			}
			res = append(res, m)
		}
	}
	return res, nil
}
Exemplo n.º 5
0
// Get the source mount point of directory passed in as argument. Also return
// optional fields.
func getSourceMount(source string) (string, string, error) {
	// Ensure any symlinks are resolved.
	sourcePath, err := filepath.EvalSymlinks(source)
	if err != nil {
		return "", "", err
	}

	mountinfos, err := mount.GetMounts()
	if err != nil {
		return "", "", err
	}

	mountinfo := getMountInfo(mountinfos, sourcePath)
	if mountinfo != nil {
		return sourcePath, mountinfo.Optional, nil
	}

	path := sourcePath
	for {
		path = filepath.Dir(path)

		mountinfo = getMountInfo(mountinfos, path)
		if mountinfo != nil {
			return path, mountinfo.Optional, nil
		}

		if path == "/" {
			break
		}
	}

	// If we are here, we did not find parent mount. Something is wrong.
	return "", "", fmt.Errorf("Could not find source mount of %s", source)
}
Exemplo n.º 6
0
// storageHealthCheck returns a channel that will close when the distributed
// storage is no longer accessible (i.e., stale NFS mount)
func (c *Controller) storageHealthCheck() (chan struct{}, error) {
	gone := make(chan struct{})
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	nfsMountPoints := []string{}
	for _, minfo := range mounts {
		if strings.HasPrefix(minfo.Fstype, "nfs") {
			nfsMountPoints = append(nfsMountPoints, minfo.Mountpoint)
		}
	}
	if len(nfsMountPoints) > 0 {
		// Start polling
		go func() {
			for {
				for _, mp := range nfsMountPoints {
					if isNFSMountStale(mp) {
						close(gone)
						return
					}
				}
				<-time.After(5 * time.Second)
			}
		}()
	}
	return gone, nil
}
Exemplo n.º 7
0
// Get the parent mount point of directory passed in as argument. Also return
// optional fields.
func getParentMount(rootfs string) (string, string, error) {
	var path string

	mountinfos, err := mount.GetMounts()
	if err != nil {
		return "", "", err
	}

	mountinfo := getMountInfo(mountinfos, rootfs)
	if mountinfo != nil {
		return rootfs, mountinfo.Optional, nil
	}

	path = rootfs
	for {
		path = filepath.Dir(path)

		mountinfo = getMountInfo(mountinfos, path)
		if mountinfo != nil {
			return path, mountinfo.Optional, nil
		}

		if path == "/" {
			break
		}
	}

	// If we are here, we did not find parent mount. Something is wrong.
	return "", "", fmt.Errorf("Could not find parent mount of %s", rootfs)
}
Exemplo n.º 8
0
func NewFsInfo(context Context) (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	partitions := make(map[string]partition, 0)
	fsInfo := &RealFsInfo{}
	fsInfo.labels = make(map[string]string, 0)
	supportedFsType := map[string]bool{
		// all ext systems are checked through prefix.
		"btrfs": true,
		"xfs":   true,
	}
	for _, mount := range mounts {
		if !strings.HasPrefix(mount.Fstype, "ext") && !supportedFsType[mount.Fstype] {
			continue
		}
		// Avoid bind mounts.
		if _, ok := partitions[mount.Source]; ok {
			continue
		}
		partitions[mount.Source] = partition{mount.Mountpoint, uint(mount.Major), uint(mount.Minor)}
	}
	glog.Infof("Filesystem partitions: %+v", partitions)
	fsInfo.partitions = partitions
	fsInfo.addLabels(context)
	return fsInfo, nil
}
Exemplo n.º 9
0
// New instantiates a new Root instance with the provided scope. Scope
// is the base path that the Root instance uses to store its
// volumes. The base path is created here if it does not exist.
func New(scope string, rootUID, rootGID int) (*Root, error) {
	rootDirectory := filepath.Join(scope, volumesPathName)

	if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil {
		return nil, err
	}

	r := &Root{
		scope:   scope,
		path:    rootDirectory,
		volumes: make(map[string]*localVolume),
		rootUID: rootUID,
		rootGID: rootGID,
	}

	dirs, err := ioutil.ReadDir(rootDirectory)
	if err != nil {
		return nil, err
	}

	mountInfos, err := mount.GetMounts()
	if err != nil {
		logrus.Debugf("error looking up mounts for local volume cleanup: %v", err)
	}

	for _, d := range dirs {
		if !d.IsDir() {
			continue
		}

		name := filepath.Base(d.Name())
		v := &localVolume{
			driverName: r.Name(),
			name:       name,
			path:       r.DataPath(name),
		}
		r.volumes[name] = v
		optsFilePath := filepath.Join(rootDirectory, name, "opts.json")
		if b, err := ioutil.ReadFile(optsFilePath); err == nil {
			opts := optsConfig{}
			if err := json.Unmarshal(b, &opts); err != nil {
				return nil, err
			}
			if !reflect.DeepEqual(opts, optsConfig{}) {
				v.opts = &opts
			}

			// unmount anything that may still be mounted (for example, from an unclean shutdown)
			for _, info := range mountInfos {
				if info.Mountpoint == v.path {
					mount.Unmount(v.path)
					break
				}
			}
		}
	}

	return r, nil
}
Exemplo n.º 10
0
func existsMountpointWithPrefix(mountpointPrefix string) (bool, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return false, err
	}
	for _, mnt := range mounts {
		if strings.HasPrefix(mnt.Mountpoint, mountpointPrefix) {
			return true, nil
		}
	}
	return false, nil
}
Exemplo n.º 11
0
func TestFactoryNewTmpfs(t *testing.T) {
	root, rerr := newTestRoot()
	if rerr != nil {
		t.Fatal(rerr)
	}
	defer os.RemoveAll(root)
	factory, err := New(root, Cgroupfs, TmpfsRoot)
	if err != nil {
		t.Fatal(err)
	}
	if factory == nil {
		t.Fatal("factory should not be nil")
	}
	lfactory, ok := factory.(*LinuxFactory)
	if !ok {
		t.Fatal("expected linux factory returned on linux based systems")
	}
	if lfactory.Root != root {
		t.Fatalf("expected factory root to be %q but received %q", root, lfactory.Root)
	}

	if factory.Type() != "libcontainer" {
		t.Fatalf("unexpected factory type: %q, expected %q", factory.Type(), "libcontainer")
	}
	mounted, err := mount.Mounted(lfactory.Root)
	if err != nil {
		t.Fatal(err)
	}
	if !mounted {
		t.Fatalf("Factory Root is not mounted")
	}
	mounts, err := mount.GetMounts()
	if err != nil {
		t.Fatal(err)
	}
	var found bool
	for _, m := range mounts {
		if m.Mountpoint == lfactory.Root {
			if m.Fstype != "tmpfs" {
				t.Fatalf("Fstype of root: %s, expected %s", m.Fstype, "tmpfs")
			}
			if m.Source != "tmpfs" {
				t.Fatalf("Source of root: %s, expected %s", m.Source, "tmpfs")
			}
			found = true
		}
	}
	if !found {
		t.Fatalf("Factory Root is not listed in mounts list")
	}
	defer syscall.Unmount(root, syscall.MNT_DETACH)
}
Exemplo n.º 12
0
func NewFsInfo(context Context) (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	partitions := make(map[string]partition, 0)
	fsInfo := &RealFsInfo{}
	fsInfo.labels = make(map[string]string, 0)
	supportedFsType := map[string]bool{
		// all ext systems are checked through prefix.
		"btrfs": true,
		"xfs":   true,
		"zfs":   true,
	}
	for _, mount := range mounts {
		var Fstype string
		if !strings.HasPrefix(mount.Fstype, "ext") && !supportedFsType[mount.Fstype] {
			continue
		}
		// Avoid bind mounts.
		if _, ok := partitions[mount.Source]; ok {
			continue
		}
		if mount.Fstype == "zfs" {
			Fstype = mount.Fstype
		}
		partitions[mount.Source] = partition{
			fsType:     Fstype,
			mountpoint: mount.Mountpoint,
			major:      uint(mount.Major),
			minor:      uint(mount.Minor),
		}
	}
	if storageDriver, ok := context.DockerInfo["Driver"]; ok && storageDriver == "devicemapper" {
		dev, major, minor, blockSize, err := dockerDMDevice(context.DockerInfo["DriverStatus"])
		if err != nil {
			glog.Warningf("Could not get Docker devicemapper device: %v", err)
		} else {
			partitions[dev] = partition{
				fsType:    "devicemapper",
				major:     major,
				minor:     minor,
				blockSize: blockSize,
			}
			fsInfo.labels[LabelDockerImages] = dev
		}
	}
	glog.Infof("Filesystem partitions: %+v", partitions)
	fsInfo.partitions = partitions
	fsInfo.addLabels(context)
	return fsInfo, nil
}
Exemplo n.º 13
0
// Take care of the old 1.11.0 behavior in case the version upgrade
// happened without a clean daemon shutdown
func (clnt *client) cleanupOldRootfs(containerID string) {
	// Unmount and delete the bundle folder
	if mts, err := mount.GetMounts(); err == nil {
		for _, mts := range mts {
			if strings.HasSuffix(mts.Mountpoint, containerID+"/rootfs") {
				if err := syscall.Unmount(mts.Mountpoint, syscall.MNT_DETACH); err == nil {
					os.RemoveAll(strings.TrimSuffix(mts.Mountpoint, "/rootfs"))
				}
				break
			}
		}
	}
}
Exemplo n.º 14
0
func getMount(mountpoint string) (*mount.Info, error) {
	entries, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}

	// Search the table for the mountpoint
	for _, e := range entries {
		if e.Mountpoint == mountpoint {
			return e, nil
		}
	}
	return nil, fmt.Errorf("Mountpoint not found")
}
Exemplo n.º 15
0
func FindCgroupMountpointDir() (string, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return "", err
	}

	for _, mount := range mounts {
		if mount.Fstype == "cgroup" {
			return filepath.Dir(mount.Mountpoint), nil
		}
	}

	return "", NewNotFoundError("cgroup")
}
Exemplo n.º 16
0
Arquivo: mount.go Projeto: yuuki/droot
func (m *Mounter) GetMountsRoot() ([]*mount.Info, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}

	targets := make([]*mount.Info, 0)
	for _, mo := range mounts {
		if strings.HasPrefix(mo.Mountpoint, m.rootDir) {
			targets = append(targets, mo)
		}
	}

	return targets, nil
}
Exemplo n.º 17
0
func NewFsInfo(context Context) (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	fsInfo := &RealFsInfo{
		partitions: make(map[string]partition, 0),
		labels:     make(map[string]string, 0),
		dmsetup:    devicemapper.NewDmsetupClient(),
	}

	supportedFsType := map[string]bool{
		// all ext systems are checked through prefix.
		"btrfs": true,
		"xfs":   true,
		"zfs":   true,
	}
	for _, mount := range mounts {
		var Fstype string
		if !strings.HasPrefix(mount.Fstype, "ext") && !supportedFsType[mount.Fstype] {
			continue
		}
		// Avoid bind mounts.
		if _, ok := fsInfo.partitions[mount.Source]; ok {
			continue
		}
		if mount.Fstype == "zfs" {
			Fstype = mount.Fstype
		}
		fsInfo.partitions[mount.Source] = partition{
			fsType:     Fstype,
			mountpoint: mount.Mountpoint,
			major:      uint(mount.Major),
			minor:      uint(mount.Minor),
		}
	}

	fsInfo.addRktImagesLabel(context, mounts)
	// need to call this before the log line below printing out the partitions, as this function may
	// add a "partition" for devicemapper to fsInfo.partitions
	fsInfo.addDockerImagesLabel(context, mounts)
	//add hyper images label
	fsInfo.addHyperImagesLabel(context, mounts)
	glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
	fsInfo.addSystemRootLabel(mounts)
	return fsInfo, nil
}
Exemplo n.º 18
0
// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
func FindCgroupMountpoint(subsystem string) (string, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return "", err
	}

	for _, mount := range mounts {
		if mount.Fstype == "cgroup" {
			for _, opt := range strings.Split(mount.VfsOpts, ",") {
				if opt == subsystem {
					return mount.Mountpoint, nil
				}
			}
		}
	}
	return "", ErrNotFound
}
Exemplo n.º 19
0
func NewFsInfo() (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	partitions := make(map[string]partition, 0)
	for _, mount := range mounts {
		if !strings.HasPrefix(mount.Fstype, "ext") {
			continue
		}
		// Avoid bind mounts.
		if _, ok := partitions[mount.Source]; ok {
			continue
		}
		partitions[mount.Source] = partition{mount.Mountpoint, uint(mount.Major), uint(mount.Minor)}
	}
	return &RealFsInfo{partitions}, nil
}
Exemplo n.º 20
0
func NewFsInfo(context Context) (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	fsInfo := &RealFsInfo{
		partitions: make(map[string]partition, 0),
		labels:     make(map[string]string, 0),
		dmsetup:    &defaultDmsetupClient{},
	}

	fsInfo.addSystemRootLabel(mounts)
	fsInfo.addDockerImagesLabel(context, mounts)
	fsInfo.addRktImagesLabel(context, mounts)

	supportedFsType := map[string]bool{
		// all ext systems are checked through prefix.
		"btrfs": true,
		"xfs":   true,
		"zfs":   true,
	}
	for _, mount := range mounts {
		var Fstype string
		if !strings.HasPrefix(mount.Fstype, "ext") && !supportedFsType[mount.Fstype] {
			continue
		}
		// Avoid bind mounts.
		if _, ok := fsInfo.partitions[mount.Source]; ok {
			continue
		}
		if mount.Fstype == "zfs" {
			Fstype = mount.Fstype
		}
		fsInfo.partitions[mount.Source] = partition{
			fsType:     Fstype,
			mountpoint: mount.Mountpoint,
			major:      uint(mount.Major),
			minor:      uint(mount.Minor),
		}
	}

	glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
	return fsInfo, nil
}
Exemplo n.º 21
0
// Load mount table
func (m *NFSMounter) Load(source string) error {
	info, err := mount.GetMounts()
	if err != nil {
		return err
	}
	re := regexp.MustCompile(`,addr=(.*)`)
MountLoop:
	for _, v := range info {
		if m.server != "" {
			if v.Fstype != "nfs" {
				continue
			}
			matches := re.FindStringSubmatch(v.VfsOpts)
			if len(matches) != 2 {
				continue
			}
			if matches[1] != m.server {
				continue
			}
		}
		mount, ok := m.mounts[v.Source]
		if !ok {
			mount = &Info{
				Device:     v.Source,
				Fs:         v.Fstype,
				Minor:      v.Minor,
				Mountpoint: make([]*PathInfo, 0),
			}
			m.mounts[v.Source] = mount
		}
		// Allow Load to be called multiple times.
		for _, p := range mount.Mountpoint {
			if p.Path == v.Mountpoint {
				continue MountLoop
			}
		}
		// XXX Reconstruct refs.
		mount.Mountpoint = append(mount.Mountpoint, &PathInfo{Path: v.Mountpoint, ref: 1})
	}
	return nil
}
Exemplo n.º 22
0
Arquivo: os.go Projeto: Oskoss/rexray
func (driver *Driver) GetMounts(deviceName, mountPoint string) ([]*mount.Info, error) {

	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}

	if mountPoint == "" && deviceName == "" {
		return mounts, nil
	} else if mountPoint != "" && deviceName != "" {
		return nil, errors.New("Cannot specify mountPoint and deviceName")
	}

	var matchedMounts []*mount.Info
	for _, mount := range mounts {
		if mount.Mountpoint == mountPoint || mount.Source == deviceName {
			matchedMounts = append(matchedMounts, mount)
		}
	}
	return matchedMounts, nil
}
Exemplo n.º 23
0
func newFsInfo(storageDriver string) (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	fsInfo := &RealFsInfo{
		partitions: make(map[string]partition, 0),
		labels:     make(map[string]string, 0),
	}

	fsInfo.addSystemRootLabel(mounts)

	supportedFsType := map[string]bool{
		// all ext systems are checked through prefix.
		"btrfs": true,
		"xfs":   true,
		"zfs":   true,
	}
	for _, mount := range mounts {
		var Fstype string
		if !strings.HasPrefix(mount.Fstype, "ext") && !supportedFsType[mount.Fstype] {
			continue
		}
		// Avoid bind mounts.
		if _, ok := fsInfo.partitions[mount.Source]; ok {
			continue
		}
		if mount.Fstype == "zfs" {
			Fstype = mount.Fstype
		}
		fsInfo.partitions[mount.Source] = partition{
			fsType:     Fstype,
			mountpoint: mount.Mountpoint,
			major:      uint(mount.Major),
			minor:      uint(mount.Minor),
		}
	}

	return fsInfo, nil
}
Exemplo n.º 24
0
func NewFsInfo(context Context) (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}

	// Avoid devicemapper container mounts - these are tracked by the ThinPoolWatcher
	excluded := []string{fmt.Sprintf("%s/devicemapper/mnt", context.Docker.Root)}
	fsInfo := &RealFsInfo{
		partitions: processMounts(mounts, excluded),
		labels:     make(map[string]string, 0),
		dmsetup:    devicemapper.NewDmsetupClient(),
	}

	fsInfo.addRktImagesLabel(context, mounts)
	// need to call this before the log line below printing out the partitions, as this function may
	// add a "partition" for devicemapper to fsInfo.partitions
	fsInfo.addDockerImagesLabel(context, mounts)

	glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
	fsInfo.addSystemRootLabel(mounts)
	return fsInfo, nil
}
Exemplo n.º 25
0
func NewFsInfo(context Context) (FsInfo, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}
	partitions := make(map[string]partition, 0)
	fsInfo := &RealFsInfo{}
	fsInfo.labels = make(map[string]string, 0)
	for _, mount := range mounts {
		if !strings.HasPrefix(mount.Fstype, "ext") && mount.Fstype != "btrfs" {
			continue
		}
		// Avoid bind mounts.
		if _, ok := partitions[mount.Source]; ok {
			continue
		}
		partitions[mount.Source] = partition{mount.Mountpoint, uint(mount.Major), uint(mount.Minor)}
	}
	glog.Infof("Filesystem partitions: %+v", partitions)
	fsInfo.partitions = partitions
	fsInfo.addLabels(context)
	return fsInfo, nil
}
Exemplo n.º 26
0
func lookupZfsDataset(rootdir string) (string, error) {
	var stat syscall.Stat_t
	if err := syscall.Stat(rootdir, &stat); err != nil {
		return "", fmt.Errorf("Failed to access '%s': %s", rootdir, err)
	}
	wantedDev := stat.Dev

	mounts, err := mount.GetMounts()
	if err != nil {
		return "", err
	}
	for _, m := range mounts {
		if err := syscall.Stat(m.Mountpoint, &stat); err != nil {
			logrus.Debugf("[zfs] failed to stat '%s' while scanning for zfs mount: %v", m.Mountpoint, err)
			continue // may fail on fuse file systems
		}

		if stat.Dev == wantedDev && m.Fstype == "zfs" {
			return m.Source, nil
		}
	}

	return "", fmt.Errorf("Failed to find zfs dataset mounted on '%s' in /proc/mounts", rootdir)
}
Exemplo n.º 27
0
func TestCreateWithOpts(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Skip()
	}

	rootDir, err := ioutil.TempDir("", "local-volume-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(rootDir)

	r, err := New(rootDir, 0, 0)
	if err != nil {
		t.Fatal(err)
	}

	if _, err := r.Create("test", map[string]string{"invalidopt": "notsupported"}); err == nil {
		t.Fatal("expected invalid opt to cause error")
	}

	vol, err := r.Create("test", map[string]string{"device": "tmpfs", "type": "tmpfs", "o": "size=1m,uid=1000"})
	if err != nil {
		t.Fatal(err)
	}
	v := vol.(*localVolume)

	dir, err := v.Mount("1234")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := v.Unmount("1234"); err != nil {
			t.Fatal(err)
		}
	}()

	mountInfos, err := mount.GetMounts()
	if err != nil {
		t.Fatal(err)
	}

	var found bool
	for _, info := range mountInfos {
		if info.Mountpoint == dir {
			found = true
			if info.Fstype != "tmpfs" {
				t.Fatalf("expected tmpfs mount, got %q", info.Fstype)
			}
			if info.Source != "tmpfs" {
				t.Fatalf("expected tmpfs mount, got %q", info.Source)
			}
			if !strings.Contains(info.VfsOpts, "uid=1000") {
				t.Fatalf("expected mount info to have uid=1000: %q", info.VfsOpts)
			}
			if !strings.Contains(info.VfsOpts, "size=1024k") {
				t.Fatalf("expected mount info to have size=1024k: %q", info.VfsOpts)
			}
			break
		}
	}

	if !found {
		t.Fatal("mount not found")
	}

	if v.active.count != 1 {
		t.Fatalf("Expected active mount count to be 1, got %d", v.active.count)
	}

	// test double mount
	if _, err := v.Mount("1234"); err != nil {
		t.Fatal(err)
	}
	if v.active.count != 2 {
		t.Fatalf("Expected active mount count to be 2, got %d", v.active.count)
	}

	if err := v.Unmount("1234"); err != nil {
		t.Fatal(err)
	}
	if v.active.count != 1 {
		t.Fatalf("Expected active mount count to be 1, got %d", v.active.count)
	}

	mounted, err := mount.Mounted(v.path)
	if err != nil {
		t.Fatal(err)
	}
	if !mounted {
		t.Fatal("expected mount to still be active")
	}

	r, err = New(rootDir, 0, 0)
	if err != nil {
		t.Fatal(err)
	}

	v2, exists := r.volumes["test"]
	if !exists {
		t.Fatal("missing volume on restart")
	}

	if !reflect.DeepEqual(v.opts, v2.opts) {
		t.Fatal("missing volume options on restart")
	}
}