def refresh_pacman_databases(self):
        """ Updates pacman databases """
        # Init pyalpm
        try:
            pacman = pac.Pac("/etc/pacman.conf", self.callback_queue)
        except Exception as ex:
            template = "Can't initialize pyalpm. An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(ex).__name__, ex.args)
            logging.error(message)
            raise InstallError(message)

        # Refresh pacman databases
        if not pacman.refresh():
            logging.error("Can't refresh pacman databases.")
            txt = _("Can't refresh pacman databases.")
            raise InstallError(txt)

        try:
            pacman.release()
            del pacman
        except Exception as ex:
            template = "Can't release pyalpm. An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(ex).__name__, ex.args)
            logging.error(message)
            raise InstallError(message)
Exemple #2
0
def chroot_call(cmd, chroot_dir=DEST_DIR, fatal=False, msg=None, timeout=None,
                stdin=None):
    """ Runs command inside the chroot """
    full_cmd = ['chroot', chroot_dir]

    for element in cmd:
        full_cmd.append(element)

    if not os.environ.get('CNCHI_RUNNING', False):
        os.environ['CNCHI_RUNNING'] = 'True'

    try:
        proc = subprocess.Popen(
            full_cmd,
            stdin=stdin,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        stdout_data, stderr_data = proc.communicate(timeout=timeout)
        stdout_data = stdout_data.decode().strip()
        if stdout_data:
            logging.debug(stdout_data)
        return stdout_data
    except subprocess.TimeoutExpired as err:
        if proc:
            # The child process is not killed if the timeout expires, so in
            # order to cleanup let's kill the child process and finish communication
            proc.kill()
            proc.communicate()
        msg = "Timeout running the command {0}".format(err.cmd)

        logging.error(msg)
        if fatal:
            raise InstallError(err.output)
        else:
            log_exception_info()
            return False
    except subprocess.CalledProcessError as err:
        if msg:
            msg = "{0}: {1}".format(msg, err.output)
        else:
            msg = "Error running {0}: {1}".format(err.cmd, err.output)

        logging.error(msg)
        if fatal:
            raise InstallError(err.output)
        else:
            log_exception_info()
        return False
    except OSError as os_error:
        if msg:
            msg = "{0}: {1}".format(msg, os_error)
        else:
            msg = "Error running {0}: {1}".format(" ".join(full_cmd), os_error)

        logging.error(msg)
        if fatal:
            raise InstallError(os_error)
        else:
            log_exception_info()
        return False
Exemple #3
0
    def __init__(self,
                 params,
                 prev_page="user_info",
                 next_page="slides",
                 **kwargs):
        """ Init class ui """
        super().__init__(self,
                         params,
                         name="summary",
                         prev_page=prev_page,
                         next_page=next_page,
                         **kwargs)

        self.main_window = params['main_window']

        if not self.main_window:
            raise InstallError("Can't get main window")

        scrolled_window = self.ui.get_object("scrolled_window")
        if scrolled_window:
            scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                       Gtk.PolicyType.ALWAYS)

        self.num_features = 0
        self.process = None
        self.title = _("Review")
Exemple #4
0
def sgdisk_new(device, part_num, label, size, hex_code):
    """ Helper function to call sgdisk --new (GPT) """
    # --new: Create a new partition, numbered partnum, starting at sector start
    #        and ending at sector end.
    # Parameters: partnum:start:end (zero in start or end means using default
    # value)
    # --typecode: Change a partition's GUID type code to the one specified by
    #             hexcode. Note that hexcode is a gdisk/sgdisk internal
    #             two-byte hexadecimal code.
    #             You can obtain a list of codes with the -L option.
    # Parameters: partnum:hexcode
    # --change-name: Change the name of the specified partition.
    # Parameters: partnum:name

    # logging.debug(" ".join(cmd))

    cmd = [
        'sgdisk',
        '--new={0}:0:+{1}M'.format(part_num, size),
        '--typecode={0}:{1}'.format(part_num, hex_code),
        '--change-name={0}:{1}'.format(part_num, label),
        device]
    try:
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as err:
        txt = "Cannot create a new partition on device {0}. Command {1} has failed: {2}"
        txt = txt.format(device, err.cmd, err.output.decode())
        logging.error(txt)
        txt = _(
            "Cannot create a new partition on device {0}. Command {1} has failed: {2}")
        txt = txt.format(device, err.cmd, err.output.decode())
        raise InstallError(txt)
Exemple #5
0
def parted_mkpart(device, ptype, start, end, filesystem=""):
    """ Helper function to call mkpart parted command """
    # If start is < 0 we assume we want to mkpart at the start of the disk
    if start < 0:
        start_str = "1"
    else:
        start_str = "{0}MiB".format(start)

    # -1s means "end of disk"
    if end == "-1s":
        end_str = end
    else:
        end_str = "{0}MiB".format(end)

    cmd = [
        'parted', '--align', 'optimal', '--script', device,
        '--',
        'mkpart', ptype, filesystem, start_str, end_str]

    try:
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as err:
        txt = "Cannot create a new partition on device {0}. Command {1} has failed: {2}"
        txt = txt.format(device, err.cmd, err.output.decode())
        logging.error(txt)
        txt = _(
            "Cannot create a new partition on device {0}. Command {1} has failed: {2}")
        txt = txt.format(device, err.cmd, err.output.decode())
        raise InstallError(txt)
Exemple #6
0
def popen(cmd,
          warning=True,
          error=False,
          fatal=False,
          msg=None,
          stdin=subprocess.PIPE):
    """ Helper function that calls Popen (useful if we need to use pipes) """
    try:
        proc = subprocess.Popen(cmd,
                                stdout=subprocess.PIPE,
                                stdin=stdin,
                                stderr=subprocess.STDOUT)
        return proc
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as err:
        if not msg:
            msg = "Error running {0}: {1}".format(err.cmd, err.output.decode())
        else:
            msg = "{0}: {1}".format(msg, err.output.decode())
        if not error and not fatal:
            if not warning:
                logging.debug(msg)
            else:
                logging.warning(msg)
                log_exception_info()
        else:
            logging.error(msg)
            if fatal:
                raise InstallError(msg)
            else:
                log_exception_info()
        return None
Exemple #7
0
def popen(cmd, warning=True, error=False, fatal=False, msg=None, stdin=subprocess.PIPE):
    """ Helper function that calls Popen (useful if we need to use pipes) """

    if not os.environ.get('CNCHI_RUNNING', False):
        os.environ['CNCHI_RUNNING'] = 'True'

    if not ensured_executable(cmd):
        logging.error('ensured_executable failed for cmd: %s', cmd)

    try:
        proc = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stdin=stdin,
            stderr=subprocess.STDOUT)
        return proc
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as err:
        if not msg:
            msg = "Error running {0}: {1}".format(err.cmd, err.output.decode())
        else:
            msg = "{0}: {1}".format(msg, err.output.decode())
        if not error and not fatal:
            if not warning:
                logging.debug(msg)
            else:
                logging.warning(msg)
                log_exception_info()
        else:
            logging.error(msg)
            if fatal:
                raise InstallError(msg)
            else:
                log_exception_info()
        return None
Exemple #8
0
def sgdisk(command, device):
    """ Helper function to call sgdisk (GPT) """
    cmd = ['sgdisk', "--{0}".format(command), device]
    try:
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as err:
        logging.error("Command %s failed: %s", err.cmd, err.output.decode())
        txt = _("Command {0} failed: {1}").format(err.cmd, err.output.decode())
        raise InstallError(txt)
Exemple #9
0
 def get_install_screen(self):
     """ Returns installation screen page """
     page = "installation_" + self.settings.get('partition_mode')
     install_screen = None
     try:
         install_screen = self.main_window.pages['disk_grp'][page]
     except (AttributeError, KeyError) as page_error:
         msg = "Can't find installation page called {0}: {1}"
         msg = msg.format(page, page_error)
         logging.error(msg)
         raise InstallError(msg)
     return install_screen
Exemple #10
0
def call(cmd,
         warning=True,
         error=False,
         fatal=False,
         msg=None,
         timeout=None,
         stdin=None,
         debug=True):
    """ Helper function to make a system call
    warning: If true will log a warning message if an error is detected
    error: If true will log an error message if an error is detected
    fatal: If true will log an error message AND will raise an InstallError exception
    msg: Error message to log (if empty the command called will be logged) """

    output = None

    if not os.environ.get('CNCHI_RUNNING', False):
        os.environ['CNCHI_RUNNING'] = 'True'

    if not ensured_executable(cmd):
        logging.error('ensured_executable failed for cmd: %s', cmd)

    try:
        output = subprocess.check_output(cmd,
                                         stdin=stdin,
                                         stderr=subprocess.STDOUT,
                                         timeout=timeout)
        output = output.decode()
        if output and debug:
            _output = output.strip('\n')
            logging.debug(_output)
        return output
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as err:
        err_output = err.output.decode().strip("\n")
        if not msg:
            msg = "Error running {0}: {1}".format(err.cmd, err_output)
        else:
            msg = "{0}: {1}".format(msg, err_output)
        if not error and not fatal:
            if not warning or "['umount', '-l'," in msg:
                logging.debug(msg)
            else:
                logging.warning(msg)
                log_exception_info()
        else:
            logging.error(msg)
            if fatal:
                raise InstallError(msg)
            else:
                log_exception_info()
        return False
Exemple #11
0
def parted_mklabel(device, label_type="msdos"):
    """ Helper function to call mktable parted command """

    cmd = [
        "parted", "--align", "optimal", "--script", device,
        "mklabel", label_type]

    try:
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as err:
        txt = ("Cannot create a new partition table on device {0}. "
               "Command {1} failed: {2}")
        txt = txt.format(device, err.cmd, err.output.decode())
        logging.error(txt)
        txt = _("Cannot create a new partition table on device {0}. "
                "Command {1} failed: {2}")
        txt = txt.format(device, err.cmd, err.output.decode())
        raise InstallError(txt)
Exemple #12
0
def call(cmd,
         warning=True,
         error=False,
         fatal=False,
         msg=None,
         timeout=None,
         stdin=None,
         debug=True):
    """ Helper function to make a system call
    warning: If true will log a warning message if an error is detected
    error: If true will log an error message if an error is detected
    fatal: If true will log an error message AND will raise an InstallError exception
    msg: Error message to log (if empty the command called will be logged) """

    output = None
    try:
        output = subprocess.check_output(cmd,
                                         stdin=stdin,
                                         stderr=subprocess.STDOUT,
                                         timeout=timeout)
        output = output.decode()
        if output and debug:
            output = output.strip('\n')
            logging.debug(output)
        return output
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as err:
        err_output = err.output.decode().strip("\n")
        if not msg:
            msg = "Error running {0}: {1}".format(err.cmd, err_output)
        else:
            msg = "{0}: {1}".format(msg, err_output)
        if not error and not fatal:
            if not warning:
                logging.debug(msg)
            else:
                logging.warning(msg)
                log_exception_info()
        else:
            logging.error(msg)
            if fatal:
                raise InstallError(msg)
            else:
                log_exception_info()
        return False
Exemple #13
0
    def create_zfs(self, solaris_partition_number):
        """ Setup ZFS system """

        # Empty DEST_DIR or zfs pool will fail to mount on it
        # (this will delete preexisting installing attempts, too)
        if os.path.exists(DEST_DIR):
            self.clear_dest_dir()

        device_paths = self.zfs_options["device_paths"]
        if not device_paths:
            txt = _("No devices were selected for the ZFS pool")
            raise InstallError(txt)

        # Make sure the ZFS modules are loaded
        call(["modprobe", "zfs"])

        # Using by-id (recommended) does not work atm
        # https://github.com/zfsonlinux/zfs/issues/3708

        # Can't use the whole first disk, just the dedicated zfs partition
        device_paths[0] = self.get_partition_path(device_paths[0],
                                                  solaris_partition_number)

        line = ", ".join(device_paths)
        logging.debug("Cnchi will create a ZFS pool using %s devices", line)

        # Just in case...
        if os.path.exists("/etc/zfs/zpool.cache"):
            os.remove("/etc/zfs/zpool.cache")

        try:
            os.mkdir(DEST_DIR, mode=0o755)
        except OSError:
            pass

        pool_name = self.zfs_options["pool_name"]
        pool_type = self.zfs_options["pool_type"]

        if not self.pool_name_is_valid(pool_name):
            txt = _(
                "Pool name is invalid. It must contain only alphanumeric characters (a-zA-Z0-9_), "
                "hyphens (-), colons (:), and/or spaces ( ). Names starting with the letter 'c' "
                "followed by a number (c[0-9]) are not allowed. The following names are also not "
                "allowed: 'mirror', 'raidz', 'spare', 'log'.")
            raise InstallError(txt)

        # Create zpool
        self.create_zfs_pool(pool_name, pool_type, device_paths)

        # Set the mount point of the root filesystem
        self.set_zfs_mountpoint(pool_name, "/")

        # Set the bootfs property on the descendant root filesystem so the
        # boot loader knows where to find the operating system.
        cmd = ["zpool", "set", "bootfs={0}".format(pool_name), pool_name]
        call(cmd, fatal=True)

        # Create zpool.cache file
        cmd = ["zpool", "set", "cachefile=/etc/zfs/zpool.cache", pool_name]
        call(cmd, fatal=True)

        if self.settings.get('use_home'):
            # Create home zvol
            logging.debug("Creating zfs subvolume 'home'")
            self.create_zfs_vol(pool_name, "home")
            self.set_zfs_mountpoint("{0}/home".format(pool_name), "/home")
            # ZFS automounts, we have to unmount /install/home and delete it,
            # otherwise we won't be able to import the zfs pool
            home_path = "{0}/home".format(DEST_DIR)
            call(["zfs", "umount", home_path], warning=False)
            shutil.rmtree(path=home_path, ignore_errors=True)

        # Create swap zvol (it has to be named "swap")
        swap_size = self.get_swap_size(pool_name)
        self.create_zfs_vol(pool_name, "swap", swap_size)

        # Wait until /dev initialized correct devices
        call(["udevadm", "settle"])
        call(["sync"])

        # Export the pool
        # Makes the kernel to flush all pending data to disk, writes data to
        # the disk acknowledging that the export was done, and removes all
        # knowledge that the storage pool existed in the system
        logging.debug("Exporting pool %s...", pool_name)
        cmd = ["zpool", "export", "-f", pool_name]
        call(cmd, fatal=True)

        # Let's get the id of the pool (to import it)
        pool_id, _status = self.get_pool_id(pool_name)

        if not pool_id:
            # Something bad has happened. Will use the pool name instead.
            logging.warning("Can't get %s zpool id", pool_name)
            pool_id = pool_name

        # Save pool id
        self.settings.set("zfs_pool_id", pool_id)

        # Finally, re-import the pool by-id
        # DEST_DIR must be empty or importing will fail!
        logging.debug("Importing pool %s (%s)...", pool_name, pool_id)
        cmd = [
            "zpool", "import", "-f", "-d", "/dev/disk/by-id", "-R", DEST_DIR,
            pool_id
        ]
        call(cmd, fatal=True)

        # Copy created cache file to destination
        try:
            dst_dir = os.path.join(DEST_DIR, "etc/zfs")
            os.makedirs(dst_dir, mode=0o755, exist_ok=True)
            src = "/etc/zfs/zpool.cache"
            dst = os.path.join(dst_dir, "zpool.cache")
            shutil.copyfile(src, dst)
        except OSError as copy_error:
            logging.warning(copy_error)

        # Store hostid
        hostid = call(["hostid"])
        if hostid:
            hostid_path = os.path.join(DEST_DIR, "etc/hostid")
            with open(hostid_path, "w") as hostid_file:
                hostid_file.write("{0}\n".format(hostid))
Exemple #14
0
    def create_zfs_pool(self, pool_name, pool_type, device_paths):
        """ Create zpool """

        if pool_type not in self.pool_types.values():
            raise InstallError("Unknown pool type: {0}".format(pool_type))

        #for device_path in device_paths:
        #    cmd = ["zpool", "labelclear", device_path]
        #    call(cmd)

        cmd = ["zpool", "create"]

        if self.zfs_options["force_4k"]:
            cmd.extend(["-o", "ashift=12"])

        cmd.extend(["-m", DEST_DIR, pool_name])

        pool_type = pool_type.lower().replace("-", "")

        if pool_type in ["none", "stripe"]:
            # Add first device
            cmd.append(device_paths[0])
        elif pool_type == "mirror":
            if len(device_paths) > 2 and len(device_paths) % 2 == 0:
                # Try to mirror pair of devices
                # (mirrors of two devices each)
                for i, k in zip(device_paths[0::2], device_paths[1::2]):
                    cmd.append(pool_type)
                    cmd.extend([i, k])
            else:
                cmd.append(pool_type)
                cmd.extend(device_paths)
        else:
            cmd.append(pool_type)
            cmd.extend(device_paths)

        # Wait until /dev initialized correct devices
        call(["udevadm", "settle"])
        call(["sync"])

        logging.debug("Creating zfs pool %s...", pool_name)
        if call(cmd, warning=False) is False:
            # Try again, now with -f
            cmd.insert(2, "-f")
            if call(cmd, warning=False) is False:
                # Wait 10 seconds more and try again.
                # (Waiting a bit sometimes works)
                time.sleep(10)
                call(cmd, fatal=True)

        # Wait until /dev initialized correct devices
        call(["udevadm", "settle"])
        call(["sync"])

        if pool_type == "stripe":
            # Add the other devices that were left out
            cmd = ["zpool", "add", pool_name]
            cmd.extend(device_paths[1:])
            call(cmd, fatal=True)

        logging.debug("Pool %s created.", pool_name)
Exemple #15
0
    def run(self):
        key_files = ["/tmp/.keyfile-root", "/tmp/.keyfile-home"]

        # Partition sizes are expressed in MiB
        # Get just the disk size in MiB
        device = self.auto_device
        device_name = os.path.split(device)[1]
        size_path = os.path.join("/sys/block", device_name, 'size')
        base_path = os.path.split(size_path)[0]
        if os.path.exists(size_path):
            logical_path = os.path.join(base_path, "queue/logical_block_size")
            with open(logical_path, 'r') as f:
                logical_block_size = int(f.read())
            with open(size_path, 'r') as f:
                size = int(f.read())
            disk_size = ((logical_block_size * (size - 68)) / 1024) / 1024
        else:
            logging.error("Cannot detect %s device size", device)
            txt = _(
                "Setup cannot detect size of your device, please use advanced "
                "installation routine for partitioning and mounting devices.")
            raise InstallError(txt)

        start_part_sizes = 1

        part_sizes = self.get_part_sizes(disk_size, start_part_sizes)
        self.log_part_sizes(part_sizes)

        # Disable swap and unmount all partitions inside dest_dir
        unmount_all_in_directory(self.dest_dir)
        # Disable swap and unmount all partitions of device
        unmount_all_in_device(device)
        # Remove lvm in destination device
        remove_lvm(device)
        # Close luks devices in destination device
        close_reborn_luks_devices()

        printk(False)

        # WARNING:
        # Our computed sizes are all in mebibytes (MiB) i.e. powers of 1024, not metric megabytes.
        # These are 'M' in sgdisk and 'MiB' in parted.
        # If you use 'M' in parted you'll get MB instead of MiB, and you're gonna have a bad time.

        if self.gpt:
            # Clean partition table to avoid issues!
            wrapper.sgdisk("zap-all", device)

            # Clear all magic strings/signatures - mdadm, lvm, partition tables etc.
            wrapper.dd("/dev/zero", device, bs=512, count=2048)
            wrapper.wipefs(device)

            # Create fresh GPT
            wrapper.sgdisk("clear", device)
            wrapper.parted_mklabel(device, "gpt")

            # Inform the kernel of the partition change. Needed if the hard disk had a MBR partition table.
            err_msg = "Error informing the kernel of the partition change."
            call(["partprobe", device], msg=err_msg, fatal=True)

            part_num = 1

            if not self.uefi:
                # We don't allow BIOS+GPT right now, so this code will be never executed
                # We leave here just for future reference
                # Create BIOS Boot Partition
                # GPT GUID: 21686148-6449-6E6F-744E-656564454649
                # This partition is not required if the system is UEFI based,
                # as there is no such embedding of the second-stage code in that case
                wrapper.sgdisk_new(device, part_num, "BIOS_BOOT", 2, "EF02")
                part_num += 1

            if self.bootloader == "grub2":
                # Create EFI System Partition (ESP)
                # GPT GUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
                wrapper.sgdisk_new(device, part_num, "UEFI_SYSTEM",
                                   part_sizes['efi'], "EF00")
                part_num += 1

            # Create Boot partition
            if self.bootloader in ["systemd-boot", "refind"]:
                wrapper.sgdisk_new(device, part_num, "REBORN_BOOT",
                                   part_sizes['boot'], "EF00")
            else:
                wrapper.sgdisk_new(device, part_num, "REBORN_BOOT",
                                   part_sizes['boot'], "8300")
            part_num += 1

            if self.lvm:
                # Create partition for lvm (will store root, swap and home (if desired) logical volumes)
                wrapper.sgdisk_new(device, part_num, "REBORN_LVM",
                                   part_sizes['lvm_pv'], "8E00")
                part_num += 1
            else:
                wrapper.sgdisk_new(device, part_num, "REBORN_ROOT",
                                   part_sizes['root'], "8300")
                part_num += 1
                if self.home:
                    wrapper.sgdisk_new(device, part_num, "REBORN_HOME",
                                       part_sizes['home'], "8302")
                    part_num += 1
                wrapper.sgdisk_new(device, part_num, "REBORN_SWAP", 0, "8200")

            output = call(["sgdisk", "--print", device])
            logging.debug(output)
        else:
            # DOS MBR partition table
            # Start at sector 1 for 4k drive compatibility and correct alignment
            # Clean partitiontable to avoid issues!
            wrapper.dd("/dev/zero", device, bs=512, count=2048)
            wrapper.wipefs(device)

            # Create DOS MBR
            wrapper.parted_mklabel(device, "msdos")

            # Create boot partition (all sizes are in MiB)
            # if start is -1 wrapper.parted_mkpart assumes that our partition starts at 1 (first partition in disk)
            start = -1
            end = part_sizes['boot']
            wrapper.parted_mkpart(device, "primary", start, end)

            # Set boot partition as bootable
            wrapper.parted_set(device, "1", "boot", "on")

            if self.lvm:
                # Create partition for lvm (will store root, home (if desired), and swap logical volumes)
                start = end
                # end = start + part_sizes['lvm_pv']
                end = "-1s"
                wrapper.parted_mkpart(device, "primary", start, end)

                # Set lvm flag
                wrapper.parted_set(device, "2", "lvm", "on")
            else:
                # Create root partition
                start = end
                end = start + part_sizes['root']
                wrapper.parted_mkpart(device, "primary", start, end)

                if self.home:
                    # Create home partition
                    start = end
                    end = start + part_sizes['home']
                    wrapper.parted_mkpart(device, "primary", start, end)

                # Create an extended partition where we will put our swap partition
                start = end
                # end = start + part_sizes['swap']
                end = "-1s"
                wrapper.parted_mkpart(device, "extended", start, end)

                # Now create a logical swap partition
                start += 1
                end = "-1s"
                wrapper.parted_mkpart(device, "logical", start, end,
                                      "linux-swap")

        printk(True)

        # Wait until /dev initialized correct devices
        call(["udevadm", "settle"])

        devices = self.get_devices()

        if self.gpt and self.bootloader == "grub2":
            logging.debug("EFI: %s", devices['efi'])

        logging.debug("Boot: %s", devices['boot'])
        logging.debug("Root: %s", devices['root'])

        if self.home:
            logging.debug("Home: %s", devices['home'])

        logging.debug("Swap: %s", devices['swap'])

        if self.luks:
            setup_luks(devices['luks_root'], "cryptReborn", self.luks_password,
                       key_files[0])
            if self.home and not self.lvm:
                setup_luks(devices['luks_home'], "cryptRebornHome",
                           self.luks_password, key_files[1])

        if self.lvm:
            logging.debug("Cnchi will setup LVM on device %s", devices['lvm'])

            err_msg = "Error creating LVM physical volume in device {0}"
            err_msg = err_msg.format(devices['lvm'])
            cmd = ["pvcreate", "-f", "-y", devices['lvm']]
            call(cmd, msg=err_msg, fatal=True)

            err_msg = "Error creating LVM volume group in device {0}"
            err_msg = err_msg.format(devices['lvm'])
            cmd = ["vgcreate", "-f", "-y", "RebornVG", devices['lvm']]
            call(cmd, msg=err_msg, fatal=True)

            # Fix issue 180
            # Check space we have now for creating logical volumes
            cmd = ["vgdisplay", "-c", "RebornVG"]
            vg_info = call(cmd, fatal=True)
            # Get column number 12: Size of volume group in kilobytes
            vg_size = int(vg_info.split(":")[11]) / 1024
            if part_sizes['lvm_pv'] > vg_size:
                logging.debug("Real RebornVG volume group size: %d MiB",
                              vg_size)
                logging.debug("Reajusting logical volume sizes")
                diff_size = part_sizes['lvm_pv'] - vg_size
                part_sizes = self.get_part_sizes(disk_size - diff_size,
                                                 start_part_sizes)
                self.log_part_sizes(part_sizes)

            # Create LVM volumes
            err_msg = "Error creating LVM logical volume"

            size = str(int(part_sizes['root']))
            cmd = [
                "lvcreate", "--name", "RebornRoot", "--size", size, "RebornVG"
            ]
            call(cmd, msg=err_msg, fatal=True)

            if not self.home:
                # Use the remainig space for our swap volume
                cmd = [
                    "lvcreate", "--name", "RebornSwap", "--extents",
                    "100%FREE", "RebornVG"
                ]
                call(cmd, msg=err_msg, fatal=True)
            else:
                size = str(int(part_sizes['swap']))
                cmd = [
                    "lvcreate", "--name", "RebornSwap", "--size", size,
                    "RebornVG"
                ]
                call(cmd, msg=err_msg, fatal=True)
                # Use the remaining space for our home volume
                cmd = [
                    "lvcreate", "--name", "RebornHome", "--extents",
                    "100%FREE", "RebornVG"
                ]
                call(cmd, msg=err_msg, fatal=True)

        # We have all partitions and volumes created. Let's create its filesystems with mkfs.

        mount_points = {
            'efi': '/boot/efi',
            'boot': '/boot',
            'root': '/',
            'home': '/home',
            'swap': ''
        }

        labels = {
            'efi': 'UEFI_SYSTEM',
            'boot': 'RebornBoot',
            'root': 'RebornRoot',
            'home': 'RebornHome',
            'swap': 'RebornSwap'
        }

        fs_devices = self.get_fs_devices()

        # Note: Make sure the "root" partition is defined first!
        self.mkfs(devices['root'], fs_devices[devices['root']],
                  mount_points['root'], labels['root'])
        self.mkfs(devices['swap'], fs_devices[devices['swap']],
                  mount_points['swap'], labels['swap'])

        if self.gpt and self.bootloader in ["refind", "systemd-boot"]:
            # Format EFI System Partition (ESP) with vfat (fat32)
            self.mkfs(devices['boot'], fs_devices[devices['boot']],
                      mount_points['boot'], labels['boot'], "-F 32")
        else:
            self.mkfs(devices['boot'], fs_devices[devices['boot']],
                      mount_points['boot'], labels['boot'])

        # Note: Make sure the "boot" partition is defined before the "efi" one!
        if self.gpt and self.bootloader == "grub2":
            # Format EFI System Partition (ESP) with vfat (fat32)
            self.mkfs(devices['efi'], fs_devices[devices['efi']],
                      mount_points['efi'], labels['efi'], "-F 32")

        if self.home:
            self.mkfs(devices['home'], fs_devices[devices['home']],
                      mount_points['home'], labels['home'])

        # NOTE: encrypted and/or lvm2 hooks will be added to mkinitcpio.conf in process.py if necessary
        # NOTE: /etc/default/grub, /etc/stab and /etc/crypttab will be modified in process.py, too.

        if self.luks and self.luks_password == "":
            # Copy root keyfile to boot partition and home keyfile to root partition
            # user will choose what to do with it
            # THIS IS NONSENSE (BIG SECURITY HOLE), BUT WE TRUST THE USER TO FIX THIS
            # User shouldn't store the keyfiles unencrypted unless the medium itself is reasonably safe
            # (boot partition is not)

            err_msg = "Can't copy LUKS keyfile to the installation device."
            os.chmod(key_files[0], 0o400)
            boot_path = os.path.join(self.dest_dir, "boot")
            cmd = ['mv', key_files[0], boot_path]
            call(cmd, msg=err_msg)
            if self.home and not self.lvm:
                os.chmod(key_files[1], 0o400)
                luks_dir = os.path.join(self.dest_dir, 'etc/luks-keys')
                os.makedirs(luks_dir, mode=0o755, exist_ok=True)
                cmd = ['mv', key_files[1], luks_dir]
                call(cmd, msg=err_msg)
Exemple #16
0
    def mkfs(self,
             device,
             fs_type,
             mount_point,
             label_name,
             fs_options="",
             btrfs_devices=""):
        """ We have two main cases: "swap" and everything else. """
        logging.debug("Will format device %s as %s", device, fs_type)
        if fs_type == "swap":
            err_msg = "Can't activate swap in {0}".format(device)
            swap_devices = call(["swapon", "-s"], msg=err_msg)
            if device in swap_devices:
                call(["swapoff", device], msg=err_msg)
            cmd = ["mkswap", "-L", label_name, device]
            call(cmd, msg=err_msg)
            cmd = ["swapon", device]
            call(cmd, msg=err_msg)
        else:
            mkfs = {
                "xfs":
                "mkfs.xfs {0} -L {1} -f {2}".format(fs_options, label_name,
                                                    device),
                "jfs":
                "yes | mkfs.jfs {0} -L {1} {2}".format(fs_options, label_name,
                                                       device),
                "reiserfs":
                "yes | mkreiserfs {0} -l {1} {2}".format(
                    fs_options, label_name, device),
                "ext2":
                "mkfs.ext2 -q {0} -F -L {1} {2}".format(
                    fs_options, label_name, device),
                "ext3":
                "mkfs.ext3 -q {0} -F -L {1} {2}".format(
                    fs_options, label_name, device),
                "ext4":
                "mkfs.ext4 -q {0} -F -L {1} {2}".format(
                    fs_options, label_name, device),
                "btrfs":
                "mkfs.btrfs {0} -L {1} {2}".format(fs_options, label_name,
                                                   btrfs_devices),
                "nilfs2":
                "mkfs.nilfs2 {0} -L {1} {2}".format(fs_options, label_name,
                                                    device),
                "ntfs-3g":
                "mkfs.ntfs {0} -L {1} {2}".format(fs_options, label_name,
                                                  device),
                "vfat":
                "mkfs.vfat {0} -n {1} {2}".format(fs_options, label_name,
                                                  device),
                "fat32":
                "mkfs.vfat {0} -n {1} {2}".format(fs_options, label_name,
                                                  device),
                "f2fs":
                "mkfs.f2fs {0} -l {1} {2}".format(fs_options, label_name,
                                                  device)
            }

            # Make sure the fs type is one we can handle
            if fs_type not in mkfs.keys():
                txt = _("Unknown filesystem type {0}").format(fs_type)
                raise InstallError(txt)

            command = mkfs[fs_type]

            err_msg = "Can't create filesystem {0}".format(fs_type)
            call(command.split(), msg=err_msg, fatal=True)

            # Flush filesystem buffers
            call(["sync"])

            # Create our mount directory
            path = self.dest_dir + mount_point
            os.makedirs(path, mode=0o755, exist_ok=True)

            # Mount our new filesystem

            mopts = "rw,relatime"
            if fs_type == "ext4":
                mopts = "rw,relatime,data=ordered"
            elif fs_type == "btrfs":
                mopts = 'rw,relatime,space_cache,autodefrag,inode_cache'

            err_msg = "Error trying to mount {0} in {1}".format(device, path)
            cmd = ["mount", "-t", fs_type, "-o", mopts, device, path]
            call(cmd, msg=err_msg, fatal=True)

            # Change permission of base directories to avoid btrfs issues
            if mount_point == "/tmp":
                mode = 0o1777
            elif mount_point == "/root":
                mode = 0o750
            else:
                mode = 0o755
            os.chmod(path, mode)

        fs_uuid = fs.get_uuid(device)
        fs_label = fs.get_label(device)
        msg = "Device details: %s UUID=%s LABEL=%s"
        logging.debug(msg, device, fs_uuid, fs_label)