File: Fstab.pm

package info (click to toggle)
fai 6.5.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,084 kB
  • sloc: sh: 6,774; perl: 5,665; makefile: 138
file content (419 lines) | stat: -rw-r--r-- 14,751 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
#!/usr/bin/perl -w

#*********************************************************************
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# A copy of the GNU General Public License is available as
# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
# can also obtain it by writing to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#*********************************************************************

use strict;

################################################################################
#
# @file fstab.pm
#
# @brief Generate an fstab file as appropriate for the configuration
#
# @author Christian Kern, Michael Tautschnig, Thomas Lange
#
################################################################################

package FAI;

################################################################################
#
# @brief Create a line for /etc/fstab
#
# @reference $d_ref Device reference
# @param $name Device name used as a key in /etc/fstab
# @param $dev_name Real (current) device name to be used in SWAPLIST
#
# @return fstab line
#
################################################################################
sub create_fstab_line {
  my ($d_ref, $name, $dev_name) = @_;

  my @fstab_line = ();
  my $comment_line = "";

  # add a comment denoting the actual device name in case of UUID or LABEL
  $comment_line="# device during installation: $dev_name\n"
    if ($name =~ /^(UUID|LABEL|PARTUUID|PARTLABEL)=/);

  # start with the device key
  push @fstab_line, $name;

  # add mount information, never dump, order of filesystem checks
  push @fstab_line, ($d_ref->{mountpoint}, $d_ref->{filesystem},
    $d_ref->{mount_options}, 0, 2);
  # order of filesystem checks: the root filesystem gets a 1, the others
  # get 2, swap and tmpfs get 0
  $fstab_line[-1] = 1 if ($d_ref->{mountpoint} eq "/");
  $fstab_line[-1] = 0 if ($d_ref->{filesystem} eq "swap");
  $fstab_line[-1] = 0 if ($d_ref->{filesystem} eq "tmpfs");

  # set the ROOT_PARTITION variable, if this is the mountpoint for /
  $FAI::disk_var{ROOT_PARTITION} = $name
    if ($d_ref->{mountpoint} eq "/");

  # add to the swaplist, if the filesystem is swap
  $FAI::disk_var{SWAPLIST} .= " " . $dev_name
    if ($d_ref->{filesystem} eq "swap");

  $FAI::disk_var{ESP_DEVICE} = $dev_name
    if ($d_ref->{mountpoint} eq "/boot/efi");
  $FAI::disk_var{ESP_DEVICE} = $dev_name
    if ($d_ref->{mountpoint} eq "/boot" && $d_ref->{filesystem} eq "vfat");

  my $ret = "\n$comment_line";
  # join the columns of one line with tabs
  $ret .= join ("\t", @fstab_line);
  return $ret;
}


################################################################################
#
# @brief Obtain UUID and filesystem label information, if any.
#
# @param device_name Full device name
# @param key_type Type to be used (uuid, label, or device)
#
# @return fstab key to be used
#
################################################################################
sub get_fstab_key {
  my ($device_name, $key_type) = @_;

  ("uuid" eq $key_type) or ("label" eq $key_type) or ("device" eq $key_type) or
    ("partuuid" eq $key_type) or ("partlabel" eq $key_type) or
    &FAI::internal_error("Invalid key type $key_type");

  `$FAI::udev_settle`;

  # write the device name as the first entry; if the user prefers uuids
  # or labels, use these if available
  my @uuid = ();
  &FAI::execute_ro_command(
    "/sbin/blkid -c /dev/null -s UUID -o value $device_name", \@uuid, 0);

  # every device must have a uuid, otherwise this is an error (unless we
  # are testing only)
  ($FAI::no_dry_run == 0 || scalar (@uuid) == 1)
    or die "Failed to obtain UUID for $device_name.\n
      This may happen if the device was part of a RAID array in the past;\n
      in this case run mdadm --zero-superblock $device_name and retry\n";

  # get the label -- this is likely empty; exit code 3 if no label, but that is
  # ok here
  my @label = ();
  ($key_type eq "label") && &FAI::execute_ro_command(
    "/sbin/blkid -c /dev/null -s LABEL -o value $device_name", \@label, 0);

  my @ptlabel = ();
  ($key_type eq "partlabel") && &FAI::execute_ro_command(
    "/sbin/blkid -c /dev/null -s PARTLABEL -o value $device_name", \@ptlabel, 0);

  my @ptuuid = ();
  ($key_type eq "partuuid") && &FAI::execute_ro_command(
    "/sbin/blkid -c /dev/null -s PARTUUID -o value $device_name", \@ptuuid, 0);

  # print uuid and label to console
  warn "$device_name UUID=$uuid[0]" if @uuid;
  warn "$device_name LABEL=$label[0]" if @label;
  warn "$device_name PARTUUID=$ptuuid[0]" if @ptuuid;
  warn "$device_name PARTLABEL=$ptlabel[0]" if @ptlabel;

  # using the fstabkey value the desired device entry is defined
  if ($key_type eq "uuid") {
    chomp ($uuid[0]);
    return "UUID=$uuid[0]";
  } elsif ($key_type eq "partuuid") {
    chomp($ptuuid[0]);
    return "PARTUUID=$ptuuid[0]";
  } elsif ($key_type eq "label" && scalar(@label) == 1) {
    chomp($label[0]);
    return "LABEL=$label[0]";
  } elsif ($key_type eq "partlabel" && scalar(@ptlabel) == 1) {
    chomp($ptlabel[0]);
    return "PARTLABEL=$ptlabel[0]";
  } else {
    # otherwise, use the usual device path
    return $device_name;
  }
}

################################################################################
#
# @brief Find the mount point for /boot
#
# @return mount point for /boot
#
################################################################################
sub find_boot_mnt_point {
  my %config = @_;

  my $mnt_point = ".NO_SUCH_MOUNTPOINT";

  # walk through all configured parts
  foreach my $c (keys %config) {

    if ($c =~ /^PHY_(.+)$/) {
      foreach my $p (keys %{ $config{$c}->{partitions} }) {
        my $this_mp = $config{$c}->{partitions}->{$p}->{mountpoint};

        next if (!defined($this_mp));

        return $this_mp if ($this_mp eq "/boot");
        $mnt_point = $this_mp if ($this_mp eq "/");
      }
    } elsif ($c =~ /^VG_(.+)$/) {
      next if ($1 eq "--ANY--");
      foreach my $l (keys %{ $config{$c}->{volumes} }) {
        my $this_mp = $config{$c}->{volumes}->{$l}->{mountpoint};

        next if (!defined($this_mp));

        return $this_mp if ($this_mp eq "/boot");
        $mnt_point = $this_mp if ($this_mp eq "/");
      }
    } elsif ($c eq "BTRFS") {
      foreach my $b (keys %{ $config{$c}->{volumes}}) {
        my $this_mp = $config{$c}->{volumes}->{$b}->{mountpoint};
        next if (!defined($this_mp));
        return $this_mp if ($this_mp eq "/boot");
        $mnt_point = $this_mp if ($this_mp eq "/");
      }
    } elsif ($c eq "RAID" || $c eq "CRYPT") {
      foreach my $r (keys %{ $config{$c}->{volumes} }) {
        my $this_mp = $config{$c}->{volumes}->{$r}->{mountpoint};

        next if (!defined($this_mp));

        return $this_mp if ($this_mp eq "/boot");
        $mnt_point = $this_mp if ($this_mp eq "/");
      }
    } elsif ($c eq "TMPFS") {
      # not usable for /boot
      next;
    } elsif ($c eq "NFS") {
      # not usable for /boot
      next;
    } else {
      &FAI::internal_error("Unexpected key $c");
    }
  }

  return $mnt_point;
}

################################################################################
#
# @brief this function generates the fstab file from our representation of the
# partitions to be created.
#
# @reference config Reference to our representation of the partitions to be
# created
#
# @return list of fstab lines
#
################################################################################
sub generate_fstab {

  # config structure is the only input
  my ($config) = @_;

  # the file to be returned, a list of lines
  my @fstab = ();

  # mount point for /boot
  my $boot_mnt_point = find_boot_mnt_point(%FAI::configs);

  # walk through all configured parts
  # the order of entries is most likely wrong, it is fixed at the end
  foreach my $c (keys %$config) {

    # entry is a physical device
    if ($c =~ /^PHY_(.+)$/) {
      my $device = $1;
      next if (exists($config->{$c}->{vg}) && $config->{$c}->{vg} == 1);
      # make sure the desired fstabkey is defined at all
      defined ($config->{$c}->{fstabkey})
        or &FAI::internal_error("fstabkey undefined");

      # create a line in the output file for each partition
      foreach my $p (keys %{ $config->{$c}->{partitions} }) {

        # keep a reference to save some typing
        my $p_ref = $config->{$c}->{partitions}->{$p};

        # skip extended partitions and entries without a mountpoint
        next if ($p_ref->{size}->{extended} || $p_ref->{mountpoint} eq "-");

        my $device_name = 0 == $p ? $device :
          &FAI::make_device_name($device, $p);

        # if the mount point the /boot mount point, variables must be set
        if ($p_ref->{mountpoint} eq $boot_mnt_point) {
          # set the BOOT_DEVICE and BOOT_PARTITION variables
          $FAI::disk_var{BOOT_PARTITION} = $device_name;
          $FAI::disk_var{BOOT_DEVICE} = $device;

        }

        push @fstab, create_fstab_line($p_ref,
          get_fstab_key($device_name, $config->{$c}->{fstabkey}), $device_name);

      }
    } elsif ($c =~ /^VG_(.+)$/) {
      next if ($1 eq "--ANY--");

      my $device = $1;

      # create a line in the output file for each logical volume
      foreach my $l (keys %{ $config->{$c}->{volumes} }) {

        # keep a reference to save some typing
        my $l_ref = $config->{$c}->{volumes}->{$l};

        # skip entries without a mountpoint
        next if ($l_ref->{mountpoint} eq "-");

        my $device_name = "/dev/$device/$l";

        # if the mount point the /boot mount point, variables must be set
        $FAI::disk_var{BOOT_DEVICE} = $device_name
          if ($l_ref->{mountpoint} eq $boot_mnt_point);

        push @fstab, create_fstab_line($l_ref,
          get_fstab_key($device_name, $config->{"VG_--ANY--"}->{fstabkey}), $device_name);
      }
    } elsif ($c eq "RAID") {

      # create a line in the output file for each device
      foreach my $r (keys %{ $config->{$c}->{volumes} }) {

        # keep a reference to save some typing
        my $r_ref = $config->{$c}->{volumes}->{$r};

        # skip entries without a mountpoint
        next if ($r_ref->{mountpoint} eq "-");

        my $device_name = "/dev/md$r";

        # if the mount point the /boot mount point, variables must be set
        $FAI::disk_var{BOOT_DEVICE} = $device_name
          if ($r_ref->{mountpoint} eq $boot_mnt_point);

        push @fstab, create_fstab_line($r_ref,
          get_fstab_key($device_name, $config->{RAID}->{fstabkey}), $device_name);
      }
    } elsif ($c eq "BTRFS") {
      # cycles through the volume IDs
      foreach my $v (keys %{ $config->{$c}->{volumes} }) {

        # skip entries without a mountpoint
        next if ( $config->{$c}->{volumes}->{$v}->{mountpoint} eq "-");

        $FAI::disk_var{BOOT_DEVICE} = (keys %{$config->{$c}->{volumes}->{$v}->{devices}})[0]
          if ( $config->{$c}->{volumes}->{$v}->{mountpoint} eq $boot_mnt_point);

        # get an array of devices that are part of the BTRFS RAID configuration
        my @device_names = keys %{ $config->{$c}->{volumes}->{$v}->{devices}};
        my $name_string = join(".", @device_names);

        # Only one of the BTRFS RAID devices are necessary to get the fstab key

        if (defined($config->{"BTRFS"}->{fstabkey})) {
          push @fstab, &FAI::create_fstab_line($config->{$c}->{volumes}->{$v},
                                               &FAI::get_fstab_key($device_names[0], $config->{"BTRFS"}->{fstabkey}), $name_string);
        } else {
          push @fstab, &FAI::create_fstab_line($config->{$c}->{volumes}->{$v},
                                               &FAI::get_fstab_key($device_names[0], $config->{$c}->{volumes}->{$v}->{fstabkey}), $device_names[0]);
        }
      }
    } elsif ($c eq "CRYPT") {
      foreach my $v (keys %{ $config->{$c}->{volumes} }) {
        my $c_ref = $config->{$c}->{volumes}->{$v};

        next if ($c_ref->{mountpoint} eq "-");

        my $device_name = &FAI::enc_name($c_ref->{device});

        ($c_ref->{mountpoint} eq $boot_mnt_point) and
          die "Boot partition cannot be encrypted\n";

        push @fstab, create_fstab_line($c_ref, $device_name, $device_name);
      }
    } elsif ($c eq "TMPFS") {
      foreach my $v (keys %{ $config->{$c}->{volumes} }) {
        my $c_ref = $config->{$c}->{volumes}->{$v};

        next if ($c_ref->{mountpoint} eq "-");

        ($c_ref->{mountpoint} eq $boot_mnt_point) and
          die "Boot partition cannot be a tmpfs\n";

	if (($c_ref->{mount_options} =~ m/size=/) || ($c_ref->{mount_options} =~ m/nr_blocks=/)) {
          warn "Specified tmpfs size for $c_ref->{mountpoint} ignored as mount options contain size= or nr_blocks=\n";
        } else {
          unless ($c_ref->{size} eq '-') {
            $c_ref->{mount_options} .= "," if ($c_ref->{mount_options} ne "");
            # Size will be in % or MiB
            $c_ref->{mount_options} .= "size=" . $c_ref->{size};
          }
	}

        push @fstab, create_fstab_line($c_ref, "tmpfs", "tmpfs");
      }
    } elsif ($c eq "NFS") {
      foreach my $v (keys %{ $config->{$c}->{volumes} }) {
        my $c_ref = $config->{$c}->{volumes}->{$v};

        next if ($c_ref->{mountpoint} eq "-");

        my $device_name = &FAI::enc_name($c_ref->{device});

        push @fstab, create_fstab_line($c_ref, $device_name, "nfs");
      }
    } else {
      &FAI::internal_error("Unexpected key $c");
    }
  }

  # cleanup the swaplist (remove leading space and add quotes)
  $FAI::disk_var{SWAPLIST} =~ s/^\s*/"/;
  $FAI::disk_var{SWAPLIST} =~ s/\s*$/"/;

  # cleanup the list of boot devices (remove leading space and add quotes)
  $FAI::disk_var{BOOT_DEVICE} =~ s/^\s*/"/;
  $FAI::disk_var{BOOT_DEVICE} =~ s/\s*$/"/;

  # sort the lines in @fstab to enable all sub mounts
  @fstab = sort { [split("\t",$a)]->[1] cmp [split("\t",$b)]->[1] } @fstab;

  # add a nice header to fstab
  unshift @fstab,
    "# <file sys>\t<mount point>\t<type>\t<options>\t<dump>\t<pass>";
  unshift @fstab, "#";
  unshift @fstab, "# /etc/fstab: static file system information.";

  # return the list of lines
  return @fstab;
}

1;