def test_root(self): # test umount mount(self.source, self.target, None, MS_BIND) umount(self.target) # test umount2 mount(self.source, self.target, None, MS_BIND) umount(self.target, MNT_FORCE)
def test_no_perms(self): with self.assertRaises(OSError) as cm: mount(self.source, self.target, None, MS_BIND) self.assertTrue(cm.exception.errno in (errno.EPERM, errno.EACCES)) with self.assertRaises(OSError) as cm: umount(self.target) self.assertTrue(cm.exception.errno in (errno.EPERM, errno.EINVAL))
def test_lazy_unmount(self): src_file = pjoin(self.source, 'file') bind_file = pjoin(self.target, 'file') touch(src_file) with open(src_file, 'w') as f: f.write('foo') try: with Namespace(user=True, mount=True): mount(self.source, self.target, None, MS_BIND) assert os.path.exists(bind_file) with open(bind_file) as f: # can't unmount the target due to the open file with pytest.raises(OSError) as cm: umount(self.target) assert cm.value.errno == errno.EBUSY # lazily unmount instead umount(self.target, MNT_DETACH) # confirm the file doesn't exist in the bind mount anymore assert not os.path.exists(bind_file) # but the file is still accessible to the process assert f.read() == 'foo' # trying to reopen causes IOError with pytest.raises(IOError) as cm: f = open(bind_file) assert cm.value.errno == errno.ENOENT except PermissionError: pytest.skip('No permission to use user and mount namespace')
def test_no_perms(self): with pytest.raises(OSError) as cm: mount(self.source, self.target, None, MS_BIND) assert cm.value.errno in (errno.EPERM, errno.EACCES) with pytest.raises(OSError) as cm: umount(self.target) assert cm.value.errno in (errno.EPERM, errno.EINVAL)
def test_args_bytes(self): # The initial source, target, and fstype arguments to mount(2) must be # byte strings; if they are unicode strings the arguments get mangled # leading to errors when the syscall is run. This confirms mount() from # snakeoil.osutils always converts the arguments into byte strings. for source, target, fstype in ((b'source', b'target', b'fstype'), ('source', 'target', 'fstype')): with mock.patch('snakeoil.osutils.mount.ctypes') as mock_ctypes: with pytest.raises(OSError): mount(source, target, fstype, MS_BIND) mount_call = next(x for x in mock_ctypes.mock_calls if x[0] == 'CDLL().mount') for arg in mount_call[1][0:3]: assert isinstance(arg, bytes)
def test_bind_mount(self): src_file = pjoin(self.source, 'file') bind_file = pjoin(self.target, 'file') touch(src_file) try: with Namespace(user=True, mount=True): assert not os.path.exists(bind_file) mount(self.source, self.target, None, MS_BIND) assert os.path.exists(bind_file) umount(self.target) assert not os.path.exists(bind_file) except PermissionError: pytest.skip('No permission to use user and mount namespace')
def bind(src, dest, chroot, create=False, log=None, readonly=False, recursive=False, **_kwargs): """Set up a bind mount. :param src: The source location to mount. :type src: str :param dest: The destination to mount on. :type dest: str :param chroot: The chroot base path. :type chroot: str :param create: Whether to create the destination. :type create: bool :param log: A logger to use for logging. :type log: logging.Logger :param readonly: Whether to remount read-only. :type readonly: bool :param recursive: Whether to use a recursive bind mount. :type recursive: bool """ log = getlogger(log, __name__) fstypes = ('proc', 'sysfs', 'tmpfs') mount_flags = [] mount_options = [] if src not in fstypes: src = os.path.normpath(src) if os.path.islink(dest): dest = os.path.join(chroot, os.path.realpath(dest).lstrip('/')) if not os.path.exists(dest): create = True else: dest = os.path.normpath(dest) if create: try: if not os.path.isdir(src) and src not in fstypes: os.makedirs(os.path.dirname(dest)) else: os.makedirs(dest) except OSError as e: if e.errno != errno.EEXIST: raise if not os.path.isdir(src) and src not in fstypes: try: touch(dest) except OSError as e: raise ChrootMountError( "cannot bind mount to '{}'".format(dest), getattr(e, 'errno', None)) if not os.path.exists(src) and src not in fstypes: raise ChrootMountError("cannot bind mount from '{}'".format(src), errno.ENOENT) elif not os.path.exists(dest): raise ChrootMountError("cannot bind mount to '{}'".format(dest), errno.ENOENT) if src in fstypes: fstype = src log.debug(" mounting '{}' filesystem on '{}'".format(src, dest)) else: fstype = None mount_flags.append(MS_BIND) if recursive: mount_flags.append(MS_REC) log.debug(" bind mounting '{}' on '{}'".format(src, dest)) try: mount(source=src, target=dest, fstype=fstype, flags=reduce(operator.or_, mount_flags, 0), data=','.join(mount_options)) if readonly: mount_flags.extend([MS_REMOUNT, MS_RDONLY]) mount(source=src, target=dest, fstype=fstype, flags=reduce(operator.or_, mount_flags, 0), data=','.join(mount_options)) except OSError as e: raise ChrootMountError( 'Failed mounting: mount -t {} {} {}'.format(fstype, src, dest), e.errno)
def test_missing_dirs(self): with pytest.raises(OSError) as cm: mount('source', 'target', None, MS_BIND) assert cm.value.errno == errno.ENOENT
def test_missing_dirs(self): with self.assertRaises(OSError) as cm: mount('source', 'target', None, MS_BIND) self.assertEqual(cm.exception.errno, errno.ENOENT)
def bind(src, dest, chroot, create=False, log=None, readonly=False, recursive=False, **_kwargs): """Set up a bind mount. :param src: The source location to mount. :type src: str :param dest: The destination to mount on. :type dest: str :param chroot: The chroot base path. :type chroot: str :param create: Whether to create the destination. :type create: bool :param log: A logger to use for logging. :type log: logging.Logger :param readonly: Whether to remount read-only. :type readonly: bool :param recursive: Whether to use a recursive bind mount. :type recursive: bool """ log = getlogger(log, __name__) fstypes = ('proc', 'sysfs', 'tmpfs') mount_flags = [] mount_options = [] if src not in fstypes: src = os.path.normpath(src) if os.path.islink(dest): dest = os.path.join(chroot, os.path.realpath(dest).lstrip('/')) if not os.path.exists(dest): create = True else: dest = os.path.normpath(dest) if create: try: if not os.path.isdir(src) and src not in fstypes: os.makedirs(os.path.dirname(dest)) else: os.makedirs(dest) except OSError as e: if e.errno != errno.EEXIST: raise if not os.path.isdir(src) and src not in fstypes: try: touch(dest) except OSError as e: raise ChrootMountError(f'cannot bind mount to {dest!r}', getattr(e, 'errno', None)) if not os.path.exists(src) and src not in fstypes: raise ChrootMountError(f'cannot bind mount from {src!r}', errno.ENOENT) elif not os.path.exists(dest): raise ChrootMountError(f'cannot bind mount to {dest!r}', errno.ENOENT) if src in fstypes: fstype = src log.debug(' mounting %r filesystem on %r', src, dest) else: fstype = None mount_flags.append(MS_BIND) if recursive: mount_flags.append(MS_REC) log.debug(' bind mounting %r on %r', src, dest) try: mount(source=src, target=dest, fstype=fstype, flags=reduce(operator.or_, mount_flags, 0), data=','.join(mount_options)) if readonly: mount_flags.extend([MS_REMOUNT, MS_RDONLY]) mount(source=src, target=dest, fstype=fstype, flags=reduce(operator.or_, mount_flags, 0), data=','.join(mount_options)) except OSError as e: raise ChrootMountError( f'failed mounting: mount -t {fstype} {src} {dest}', e.errno)