source: calamares/trunk/fuentes/src/modules/fstab/main.py @ 7538

Last change on this file since 7538 was 7538, checked in by kbut, 13 months ago

sync with github

File size: 10.5 KB
Line 
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# === This file is part of Calamares - <https://github.com/calamares> ===
5#
6#   Copyright 2014, Aurélien Gâteau <agateau@kde.org>
7#   Copyright 2016, Teo Mrnjavac <teo@kde.org>
8#   Copyright 2017, Alf Gaida <agaida@siduction.org>
9#
10#   Calamares is free software: you can redistribute it and/or modify
11#   it under the terms of the GNU General Public License as published by
12#   the Free Software Foundation, either version 3 of the License, or
13#   (at your option) any later version.
14#
15#   Calamares is distributed in the hope that it will be useful,
16#   but WITHOUT ANY WARRANTY; without even the implied warranty of
17#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18#   GNU General Public License for more details.
19#
20#   You should have received a copy of the GNU General Public License
21#   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
22
23import os
24import re
25import subprocess
26
27import libcalamares
28
29
30FSTAB_HEADER = """# /etc/fstab: static file system information.
31#
32# Use 'blkid' to print the universally unique identifier for a device; this may
33# be used with UUID= as a more robust way to name devices that works even if
34# disks are added and removed. See fstab(5).
35#
36# <file system>             <mount point>  <type>  <options>  <dump>  <pass>"""
37
38CRYPTTAB_HEADER = """# /etc/crypttab: mappings for encrypted partitions.
39#
40# Each mapped device will be created in /dev/mapper, so your /etc/fstab
41# should use the /dev/mapper/<name> paths for encrypted devices.
42#
43# See crypttab(5) for the supported syntax.
44#
45# NOTE: Do not list your root (/) partition here, it must be set up
46#       beforehand by the initramfs (/etc/mkinitcpio.conf). The same applies
47#       to encrypted swap, which should be set up with mkinitcpio-openswap
48#       for resume support.
49#
50# <name>               <device>                         <password> <options>"""
51
52# Turn Parted filesystem names into fstab names
53FS_MAP = {
54    "fat16": "vfat",
55    "fat32": "vfat",
56    "linuxswap": "swap",
57}
58
59
60def mkdir_p(path):
61    """ Create directory.
62
63    :param path:
64    """
65    if not os.path.exists(path):
66        os.makedirs(path)
67
68
69def is_ssd_disk(disk_name):
70    """ Checks if given disk is actually a ssd disk.
71
72    :param disk_name:
73    :return:
74    """
75    filename = os.path.join("/sys/block", disk_name, "queue/rotational")
76
77    if not os.path.exists(filename):
78        # Should not happen unless sysfs changes, but better safe than sorry
79        return False
80
81    with open(filename) as sysfile:
82        return sysfile.read() == "0\n"
83
84
85def disk_name_for_partition(partition):
86    """ Returns disk name for each found partition.
87
88    :param partition:
89    :return:
90    """
91    name = os.path.basename(partition["device"])
92
93    if name.startswith("/dev/mmcblk") or name.startswith("/dev/nvme"):
94        return re.sub("p[0-9]+$", "", name)
95
96    return re.sub("[0-9]+$", "", name)
97
98
99class FstabGenerator(object):
100    """ Class header
101
102    :param partitions:
103    :param root_mount_point:
104    :param mount_options:
105    :param ssd_extra_mount_options:
106    """
107    def __init__(self, partitions, root_mount_point, mount_options,
108                 ssd_extra_mount_options, crypttab_options):
109        self.partitions = partitions
110        self.root_mount_point = root_mount_point
111        self.mount_options = mount_options
112        self.ssd_extra_mount_options = ssd_extra_mount_options
113        self.crypttab_options = crypttab_options
114        self.ssd_disks = set()
115        self.root_is_ssd = False
116
117    def run(self):
118        """ Calls needed sub routines.
119
120        :return:
121        """
122        self.find_ssd_disks()
123        self.generate_fstab()
124        self.generate_crypttab()
125        self.create_mount_points()
126
127        return None
128
129    def find_ssd_disks(self):
130        """ Checks for ssd disks """
131        disks = {disk_name_for_partition(x) for x in self.partitions}
132        self.ssd_disks = {x for x in disks if is_ssd_disk(x)}
133
134    def generate_crypttab(self):
135        """ Create crypttab. """
136        mkdir_p(os.path.join(self.root_mount_point, "etc"))
137        crypttab_path = os.path.join(self.root_mount_point, "etc", "crypttab")
138
139        with open(crypttab_path, "w") as crypttab_file:
140            print(CRYPTTAB_HEADER, file=crypttab_file)
141
142            for partition in self.partitions:
143                dct = self.generate_crypttab_line_info(partition)
144
145                if dct:
146                    self.print_crypttab_line(dct, file=crypttab_file)
147
148    def generate_crypttab_line_info(self, partition):
149        """ Generates information for each crypttab entry. """
150        if "luksMapperName" not in partition or "luksUuid" not in partition:
151            return None
152
153        mapper_name = partition["luksMapperName"]
154        mount_point = partition["mountPoint"]
155        luks_uuid = partition["luksUuid"]
156        if not mapper_name or not luks_uuid:
157            return None
158
159        return dict(
160            name=mapper_name,
161            device="UUID=" + luks_uuid,
162            password="/crypto_keyfile.bin",
163            options=self.crypttab_options,
164        )
165
166    def print_crypttab_line(self, dct, file=None):
167        """ Prints line to '/etc/crypttab' file. """
168        line = "{:21} {:<45} {} {}".format(dct["name"],
169                                           dct["device"],
170                                           dct["password"],
171                                           dct["options"],
172                                           )
173
174        print(line, file=file)
175
176    def generate_fstab(self):
177        """ Create fstab. """
178        mkdir_p(os.path.join(self.root_mount_point, "etc"))
179        fstab_path = os.path.join(self.root_mount_point, "etc", "fstab")
180
181        with open(fstab_path, "w") as fstab_file:
182            print(FSTAB_HEADER, file=fstab_file)
183
184            for partition in self.partitions:
185                # Special treatment for a btrfs root with @ and @home
186                # subvolumes
187                if (partition["fs"] == "btrfs"
188                   and partition["mountPoint"] == "/"):
189                    output = subprocess.check_output(['btrfs',
190                                                      'subvolume',
191                                                      'list',
192                                                      self.root_mount_point])
193                    output_lines = output.splitlines()
194                    for line in output_lines:
195                        if line.endswith(b'path @'):
196                            root_entry = partition
197                            root_entry["subvol"] = "@"
198                            dct = self.generate_fstab_line_info(root_entry)
199                            if dct:
200                                self.print_fstab_line(dct, file=fstab_file)
201                        elif line.endswith(b'path @home'):
202                            home_entry = partition
203                            home_entry["mountPoint"] = "/home"
204                            home_entry["subvol"] = "@home"
205                            dct = self.generate_fstab_line_info(home_entry)
206                            if dct:
207                                self.print_fstab_line(dct, file=fstab_file)
208
209                else:
210                    dct = self.generate_fstab_line_info(partition)
211
212                    if dct:
213                        self.print_fstab_line(dct, file=fstab_file)
214
215            if self.root_is_ssd:
216                # Mount /tmp on a tmpfs
217                dct = dict(device="tmpfs",
218                           mount_point="/tmp",
219                           fs="tmpfs",
220                           options="defaults,noatime,mode=1777",
221                           check=0,
222                           )
223                self.print_fstab_line(dct, file=fstab_file)
224
225    def generate_fstab_line_info(self, partition):
226        """ Generates information for each fstab entry. """
227        filesystem = partition["fs"].lower()
228        has_luks = "luksMapperName" in partition
229        mount_point = partition["mountPoint"]
230        disk_name = disk_name_for_partition(partition)
231        is_ssd = disk_name in self.ssd_disks
232        filesystem = FS_MAP.get(filesystem, filesystem)
233
234        if not mount_point and not filesystem == "swap":
235            return None
236        if not mount_point:
237            mount_point = "swap"
238
239        options = self.mount_options.get(filesystem,
240                                         self.mount_options["default"])
241        if is_ssd:
242            extra = self.ssd_extra_mount_options.get(filesystem)
243
244            if extra:
245                options += "," + extra
246
247        if mount_point == "/":
248            check = 1
249        elif mount_point:
250            check = 2
251        else:
252            check = 0
253
254        if mount_point == "/":
255            self.root_is_ssd = is_ssd
256
257        if filesystem == "btrfs" and "subvol" in partition:
258            options="subvol={},".format(partition["subvol"]) + options
259
260        if has_luks:
261            device="/dev/mapper/" + partition["luksMapperName"]
262        else:
263            device="UUID=" + partition["uuid"]
264
265        return dict(device=device,
266                    mount_point=mount_point,
267                    fs=filesystem,
268                    options=options,
269                    check=check,
270                    )
271
272    def print_fstab_line(self, dct, file=None):
273        """ Prints line to '/etc/fstab' file. """
274        line = "{:41} {:<14} {:<7} {:<10} 0 {}".format(dct["device"],
275                                                       dct["mount_point"],
276                                                       dct["fs"],
277                                                       dct["options"],
278                                                       dct["check"],
279                                                       )
280        print(line, file=file)
281
282    def create_mount_points(self):
283        """ Creates mount points """
284        for partition in self.partitions:
285            if partition["mountPoint"]:
286                mkdir_p(self.root_mount_point + partition["mountPoint"])
287
288
289def run():
290    """ Configures fstab.
291
292    :return:
293    """
294    global_storage = libcalamares.globalstorage
295    conf = libcalamares.job.configuration
296    partitions = global_storage.value("partitions")
297    root_mount_point = global_storage.value("rootMountPoint")
298    mount_options = conf["mountOptions"]
299    ssd_extra_mount_options = conf.get("ssdExtraMountOptions", {})
300    crypttab_options = conf.get("crypttabOptions", "luks")
301    generator = FstabGenerator(partitions,
302                               root_mount_point,
303                               mount_options,
304                               ssd_extra_mount_options,
305                               crypttab_options)
306
307    return generator.run()
Note: See TracBrowser for help on using the repository browser.