Exemplo n.º 1
0
def get_avail_drive_letter(logger):
    """ returns a free Windows drive letter to mount image in """

    if not sys.platform == "win32":
        raise NotImplementedError("only for windows")

    # get volumes from wmic
    wmic_out = subprocess_pretty_call(
        ["wmic", "logicaldisk", "get", "caption"],
        logger,
        check=True,
        decode=True)
    volumes = [line.strip()[:-1] for line in wmic_out[1:-1]]

    # get list of network mappings
    net_out = subprocess_pretty_call(["net", "use"],
                                     logger,
                                     check=True,
                                     decode=True)
    reg = r"\s+([A-Z])\:\s+\\"
    net_maps = [
        re.match(reg, line).groups()[0] for line in net_out
        if re.match(reg, line)
    ]

    # merge and sort both volumes and network shares
    used = sorted(list(set(["A", "B", "C"] + volumes + net_maps)))

    # find the next available letter in alphabet (should be free)
    for letter in string.ascii_uppercase:
        if letter not in list(used):
            return "{}:".format(letter)
Exemplo n.º 2
0
def release_virtual_device(device, logger):
    if bool(os.getenv("NO_UDISKS", False)):
        subprocess_pretty_call([losetup_exe, "--detach", device], logger)
    else:
        subprocess_pretty_call([
            udisksctl_exe, "loop-delete", "--block-device", device, udisks_nou
        ], logger)
Exemplo n.º 3
0
def allow_write_on(fpath, logger):
    """ sets o+rw on path and return previous perms for others (0755) """

    si = os.stat(fpath)
    subprocess_pretty_call(["chmod", "-v", "o+rw", fpath],
                           logger,
                           check=True,
                           as_admin=True)

    return oct(si.st_mode)[-4:]
Exemplo n.º 4
0
def install_imdisk(logger, force=False):
    """ install imdisk manually (replicating steps from install.cmd) """

    # assume already installed
    logger.std("Looking for {}".format(imdisk_exe))
    if os.path.exists(imdisk_exe) and not force:
        logger.std("ImDisk already present.")
        return
    logger.std("Imdisk IS NOT present. Installing...")

    # install the driver and files
    cwd = os.getcwd()
    try:
        os.chdir(imdiskinst)
        ret, _ = subprocess_pretty_call(
            [
                "rundll32.exe",
                "setupapi.dll,InstallHinfSection",
                "DefaultInstall",
                "132",
                ".\\imdisk.inf",
            ],
            logger,
        )
    except Exception:
        ret = 1
    finally:
        os.chdir(cwd)

    if ret != 0:
        raise OSError("Unable to install ImDisk driver. "
                      "Please install manually from the File menu.")

    # start services
    failed = []
    for service in ("imdsksvc", "awealloc", "imdisk"):
        if subprocess_pretty_call(["net", "start", service], logger)[0] != 0:
            failed.append(service)
    if failed:
        raise OSError("ImDisk installed but some "
                      "service/driver failed to start:  {}.\n"
                      "Please install manually from the File menu.".format(
                          " ".join(failed)))

    assert os.path.exists(imdisk_exe)
Exemplo n.º 5
0
def unmount_data_partition(mount_point, device, logger):
    """ unmount data partition and free virtual resources """

    if sys.platform == "linux":

        # sleep to prevent unmount failures
        time.sleep(5)

        if mount_point:
            # unmount using device path
            if bool(os.getenv("NO_UDISKS", False)):
                subprocess_pretty_call([umount_exe, device], logger)
            else:
                subprocess_pretty_call(
                    [
                        udisksctl_exe, "unmount", "--block-device", device,
                        udisks_nou
                    ],
                    logger,
                )
            try:
                os.rmdir(mount_point)
            except (FileNotFoundError, PermissionError):
                pass

        # delete the loop device (might have already been deleted)
        release_virtual_device(device, logger)

    elif sys.platform == "darwin":

        if mount_point:
            # unmount
            subprocess_pretty_call([umount_exe, mount_point], logger)
            try:
                os.rmdir(mount_point)
            except FileNotFoundError:
                pass
        # detach image file (also unmounts if not already done)
        subprocess_pretty_call([hdiutil_exe, "detach", device], logger)
    elif sys.platform == "win32":

        # unmount using force (-D) as -d is not reliable
        subprocess_pretty_call([imdisk_exe, "-D", "-m", device], logger)
Exemplo n.º 6
0
    def _no_udisks(logger):
        """ guess loop device w/o udisks (using losetup/root) """
        try:
            lines = subprocess_pretty_call([losetup_exe, "--find"],
                                           logger,
                                           check=True)
        except Exception as exp:
            logger.err(exp)
            return None

        return re.search(r"(\/dev\/loop[0-9]+)$",
                         lines[-1].strip()).groups()[0]
Exemplo n.º 7
0
def guess_next_loop_device(logger):
    def _no_udisks(logger):
        """ guess loop device w/o udisks (using losetup/root) """
        try:
            lines = subprocess_pretty_call([losetup_exe, "--find"],
                                           logger,
                                           check=True,
                                           decode=True)
        except Exception as exp:
            logger.err(exp)
            return None

        return re.search(r"(\/dev\/loop[0-9]+)$",
                         lines[-1].strip()).groups()[0]

    if bool(os.getenv("NO_UDISKS", False)):
        return _no_udisks(logger)

    try:
        lines = subprocess_pretty_call([udisksctl_exe, "dump"],
                                       logger,
                                       check=True,
                                       decode=True)
    except Exception as exp:
        logger.err(exp)
        return None

    devnum = None  # loopX
    devtype = None  # Block

    for line in lines:
        if line.startswith("/"):
            try:
                devnum = re.search(r"^.*loop([0-9]+)\:$", line).groups()[0]
            except Exception:
                devnum = None

        if line.startswith("  org"):
            try:
                devtype = re.search(r"\.([a-zA-Z]+)\:$", line).groups()[0]
            except Exception:
                devtype = None

        if devnum and devtype == "Block" and line.startswith("    Size:"):
            size = int(re.search(r"\s+Size\:\s+([0-9]+)$", line).groups()[0])
            if size == 0:
                return "/dev/loop{}".format(devnum)
Exemplo n.º 8
0
def mount_data_partition(image_fpath, logger):
    """ mount the QEMU image's 3rd part and return its mount point/drive """

    target_dev = get_virtual_device(image_fpath, logger)

    if sys.platform == "linux" and bool(os.getenv("NO_UDISKS", False)):
        # create a mount point in /tmp
        mount_point = tempfile.mkdtemp()

        try:
            subprocess_pretty_check_call(
                [mount_exe, "-t", "exfat", target_dev, mount_point], logger)
        except Exception:
            # ensure we release the loop device on mount failure
            unmount_data_partition(mount_point, target_dev, logger)
            raise
        return mount_point, target_dev

    elif sys.platform == "linux":
        # mount the loop-device (udisksctl sets the mount point)
        udisks_mount_ret, udisks_mount = subprocess_pretty_call(
            [udisksctl_exe, "mount", "--block-device", target_dev, udisks_nou],
            logger,
            check=False,
            decode=True,
        )
        udisks_mount = udisks_mount[0].strip()

        if udisks_mount_ret != 0 and "AlreadyMounted" in udisks_mount:
            # was automatically mounted (gnome default)
            mount_point = re.search(r"at `(\/media\/.*)'\.$",
                                    udisks_mount).groups()[0]
        elif udisks_mount_ret == 0:
            # udisksctl always mounts under /media/
            mount_point = re.search(r"at (\/media\/.+)\.$",
                                    udisks_mount).groups()[0]
        else:
            release_virtual_device(target_dev,
                                   logger)  # release loop if attached
            raise OSError("failed to mount {}".format(target_dev))

        return mount_point, target_dev

    elif sys.platform == "darwin":
        target_part = "{dev}s3".format(dev=target_dev)

        # create a mount point in /tmp
        mount_point = tempfile.mkdtemp()
        try:
            subprocess_pretty_check_call(
                [mount_exe, "-t", "exfat", target_part, mount_point], logger)
        except Exception:
            # ensure we release the loop device on mount failure
            unmount_data_partition(mount_point, target_dev, logger)
            raise
        return mount_point, target_dev

    elif sys.platform == "win32":
        mount_point = "{}\\".format(target_dev)

        # mount into the specified drive
        subprocess_pretty_check_call(
            [
                imdisk_exe,
                "-a",
                "-f",
                image_fpath,
                "-o",
                "rw",
                "-t",
                "file",
                "-v",
                "3",
                "-m",
                target_dev,
            ],
            logger,
        )
        return mount_point, target_dev
Exemplo n.º 9
0
def format_data_partition(image_fpath, logger):
    """ format the QEMU image's 3rd part in exfat on host """

    target_dev = get_virtual_device(image_fpath, logger)

    if sys.platform == "linux":

        # make sure it's not mounted (gnome automounts)
        if bool(os.getenv("NO_UDISKS", False)):
            subprocess_pretty_call([umount_exe, target_dev], logger)
        else:
            subprocess_pretty_call(
                [
                    udisksctl_exe, "unmount", "--block-device", target_dev,
                    udisks_nou
                ],
                logger,
            )

        # change mode via elevation if we can't format it
        previous_mode = None
        if not can_write_on(target_dev):
            previous_mode = allow_write_on(target_dev, logger)

        # format the data partition
        try:
            subprocess_pretty_check_call(
                [mkfs_exe, "-n", data_partition_label, target_dev], logger)
        finally:
            # remove write rights we just added
            if previous_mode:
                restore_mode(target_dev, previous_mode, logger)

            # ensure we release the loop device on mount failure
            unmount_data_partition(None, target_dev, logger)

    elif sys.platform == "darwin":
        target_part = "{dev}s3".format(dev=target_dev)

        try:
            subprocess_pretty_check_call(
                [
                    diskutil_exe,
                    "eraseVolume",
                    "exfat",
                    data_partition_label,
                    target_part,
                ],
                logger,
            )
        finally:
            # ensure we release the loop device on mount failure
            unmount_data_partition(None, target_dev, logger)

    elif sys.platform == "win32":
        # mount into specified path AND format
        try:
            subprocess_pretty_check_call(
                [
                    imdisk_exe,
                    "-a",
                    "-f",
                    image_fpath,
                    "-o",
                    "rw",
                    "-t",
                    "file",
                    "-v",
                    "3",
                    "-p",
                    "/fs:exfat /V:{} /q /y".format(data_partition_label),
                    "-m",
                    target_dev,
                ],
                logger,
            )
        finally:
            # ensure we release the loop device on mount failure
            unmount_data_partition(None, target_dev, logger)
Exemplo n.º 10
0
def get_virtual_device(image_fpath, logger):
    """ create and return a loop device or drive letter we can format/mount """

    if sys.platform == "linux":
        # find out offset for third partition from the root part size
        base_image = get_content("hotspot_master_image")
        disk_size = get_qemu_image_size(image_fpath, logger)
        offset, size = get_start_offset(base_image.get("root_partition_size"),
                                        disk_size)

        # prepare loop device
        if bool(os.getenv("NO_UDISKS", False)):
            loop_maker = subprocess_pretty_call(
                [
                    "/sbin/losetup",
                    "--offset",
                    str(offset),
                    "--sizelimit",
                    str(size),
                    "--find",
                    "--show",
                    image_fpath,
                ],
                logger,
                check=True,
                decode=True,
            )[0].strip()
        else:
            loop_maker = subprocess_pretty_call(
                [
                    udisksctl_exe,
                    "loop-setup",
                    "--offset",
                    str(offset),
                    "--size",
                    str(size),
                    "--file",
                    image_fpath,
                    udisks_nou,
                ],
                logger,
                check=True,
                decode=True,
            )[0].strip()

        target_dev = re.search(r"(\/dev\/loop[0-9]+)\.?$",
                               loop_maker).groups()[0]

    elif sys.platform == "darwin":
        # attach image to create loop devices
        hdiutil_out = subprocess_pretty_call(
            [hdiutil_exe, "attach", "-nomount", image_fpath],
            logger,
            check=True,
            decode=True,
        )[0].strip()
        target_dev = str(hdiutil_out.splitlines()[0].split()[0])

    elif sys.platform == "win32":
        # make sure we have imdisk installed
        install_imdisk(logger)

        # get an available letter
        target_dev = get_avail_drive_letter(logger)

    return target_dev
Exemplo n.º 11
0
def restore_mode(fpath, mode, logger):
    """ sets specified mode to specified file """
    subprocess_pretty_call(["chmod", "-v", mode, fpath],
                           logger,
                           check=True,
                           as_admin=True)