/[smeserver]/rpms/e-smith-backup/sme10/e-smith-backup-2.6.0-bz9299-consoleBackupBlockDevice.patch
ViewVC logotype

Contents of /rpms/e-smith-backup/sme10/e-smith-backup-2.6.0-bz9299-consoleBackupBlockDevice.patch

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations) (download)
Sat Apr 15 03:54:54 2017 UTC (7 years, 7 months ago) by unnilennium
Branch: MAIN
CVS Tags: e-smith-backup-2_6_0-24_el7_sme, e-smith-backup-2_6_0-19_el7_sme, e-smith-backup-2_6_0-17_el7_sme, e-smith-backup-2_6_0-30_el7_sme, e-smith-backup-2_6_0-29_el7_sme, e-smith-backup-2_6_0-9_el7_sme, e-smith-backup-2_6_0-15_el7_sme, e-smith-backup-2_6_0-18_el7_sme, e-smith-backup-2_6_0-16_el7_sme, e-smith-backup-2_6_0-11_el7_sme, e-smith-backup-2_6_0-14_el7_sme, e-smith-backup-2_6_0-20_el7_sme, e-smith-backup-2_6_0-23_el7_sme, e-smith-backup-2_6_0-10_el7_sme, e-smith-backup-2_6_0-21_el7_sme, e-smith-backup-2_6_0-13_el7_sme, e-smith-backup-2_6_0-28_el7_sme, e-smith-backup-2_6_0-12_el7_sme, e-smith-backup-2_6_0-22_el7_sme, e-smith-backup-2_6_0-27_el7_sme, e-smith-backup-2_6_0-25_el7_sme, e-smith-backup-2_6_0-26_el7_sme, HEAD
Changes since 1.1: +1 -1 lines
* Fri Apr 14 2017 Jean-Philipe Pialasse <tests@pialasse.com> 2.6.0-9.sme
- added support back to ext2 and ext3 [SME: 9299]

1 --- e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_backup.pm.devdetection 2017-02-17 14:37:15.000000000 -0500
2 +++ e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_backup.pm 2017-02-24 14:46:06.777798717 -0500
3 @@ -6,8 +6,14 @@
4 use esmith::util;
5 use Locale::gettext;
6 use esmith::Backup;
7 -#use Filesys::DiskFree;
8 -#use Sys::Filesystem;
9 +use Carp;
10 +use feature qw( say );
11 +use esmith::BlockDevices;
12 +use POSIX qw(:sys_wait_h strftime);
13 +use File::stat;
14 +use Taint::Util;
15 +
16 +my $EMPTY = q{};
17
18 sub new
19 {
20 @@ -43,22 +49,10 @@
21 my $fh = shift;
22 my @backup_list = esmith::Backup->restore_list;
23 my @backup_excludes = esmith::Backup->excludes;
24 -
25 - unless (open(DU, "-|"))
26 - {
27 - open(STDERR, ">/dev/null");
28 - exec qw(/usr/bin/du -sb), map { "/$_" } @backup_list;
29 - }
30 - my $backup_size = 0;
31 - while (<DU>)
32 - {
33 - next unless (/^(\d+)/);
34 - $backup_size += $1;
35 - }
36 - close DU;
37 + my $backup_size = backupSize (@backup_list);
38
39 open(OLDSTDOUT, ">&STDOUT");
40 - unless (open(STDOUT, ">/mnt/bootstrap-console-backup/smeserver.tgz"))
41 + unless (open(STDOUT, ">$device/smeserver.tgz"))
42 {
43 return gettext("Could not create backup file on device").": $!\n";
44 }
45 @@ -133,20 +127,85 @@
46 my ($self, $console, $db) = @_;
47 my @backup_list = esmith::Backup->restore_list;
48 my @backup_excludes = esmith::Backup->excludes;
49 + my $compressionLevel = $db->get_prop('backupconsole', 'CompressionLevel') || '-6';
50 + my $mountpoint = $db->get_prop('backupconsole', 'Mountpoint') || '/mnt/bootstrap-console-backup';
51 + my $allowMounted = $db->get_prop('backupconsole', 'AllowMounted') || 'disabled'; ### For future use
52
53 $ENV{PATH} = "/bin:/usr/bin";
54 $ENV{HOME} = "/root";
55
56 - my $compressionLevel = $db->get_prop("backupconsole", "CompressionLevel") || "-6";
57 + my $devices = esmith::BlockDevices->new ('mount' => $mountpoint, 'allowmount' => $allowMounted);
58 +
59 + INITIATE_BACKUP:
60 my ($rc, $choice) = $console->yesno_page
61 (
62 title => gettext("Create Backup to removable media"),
63 defaultno => 1,
64 - text =>
65 - gettext("Do you wish to create backup on removable media?"),
66 - );
67 - return unless $rc == 0;
68 + text => gettext('Do you wish to create a backup on removable media?')."\n\n".
69 + gettext('Insert removable media before proceeding.')."\n".
70 + gettext('It may take many seconds to scan for media.'),
71 + );
72 + if ($rc != 0)
73 + {
74 + $devices->destroy;
75 + return;
76 + }
77 + ### determine which filesystems are valid or not for backups
78 + # check expected backup size
79 + my $backup_size = backupSize (@backup_list);
80 + # validate each filesystem
81 + my ($valid, $invalid) = $devices->checkBackupDrives ($backup_size);
82 + my $text = $EMPTY;
83 +
84 + if (${$invalid}[0]) # If there are filesystems that are not valid.
85 + {
86 + $text .= gettext ('These filesystems are not valid:')."\n";
87 + foreach my $drive (sort @{$invalid})
88 + {
89 + $text .= "$drive ".$devices->desc($drive).' '.gettext ('Reason').': '.$devices->reason($drive)."\n";
90 + }
91 + $text .= "\n";
92 + }
93 + unless (${$valid}[0]) # Unless a device is found show error page
94 + {
95 + my $title = gettext('No valid backup device found').' '.gettext('size').' '.esmith::BlockDevices::scaleIt($backup_size);
96 + $text .= "\n$title, ".gettext('please try again');
97 + ($rc, $choice) = $console->yesno_page
98 + (
99 + title => $title,
100 + text => $text,
101 + left => gettext('Try again'),
102 + right => gettext('Cancel'),
103 + );
104 + if ($rc == 0) # Try Again
105 + {
106 + goto INITIATE_BACKUP;
107 + }
108 + else
109 + {
110 + $devices->destroy;
111 + return;
112 + }
113 + }
114 + $text .= gettext ('The following are valid for backup').' (';
115 + $text .= gettext ('size').' '.esmith::BlockDevices::scaleIt($backup_size).')';
116
117 + #ToDo when valid + invalid > 13 then may need to limit the information
118 + my @args = map { $_ => $devices->desc($_) } @{$valid};
119 +
120 + # Display the available backup destinations.
121 + ($rc, $choice) = $console->menu_page
122 + (
123 + title => gettext('Choose device to use for backup').' '.gettext('size').' '.esmith::BlockDevices::scaleIt($backup_size),
124 + text => $text,
125 + argsref => \@args,
126 + left => gettext('Cancel'),
127 + right => gettext('OK'),
128 + );
129 + goto INITIATE_BACKUP unless ($rc == 0);
130 + untaint $choice;
131 + $devices->mount ($choice); # mount the chosen filesystem
132 +
133 if (@backup_excludes) {
134 my $backupexclude = join ("\n/", sort @backup_excludes);
135 ($rc, $choice) = $console->yesno_page
136 @@ -159,124 +218,44 @@
137 return unless $rc == 0;
138 }
139
140 - INITIATE_BACKUP:
141 - ($rc, $choice) = $console->yesno_page
142 - (
143 - title => gettext("Insert media to use for backup"),
144 - left => gettext("Next"),
145 - right => gettext("Cancel"),
146 - text =>
147 - gettext("Insert removable media, then hit the enter key."),
148 - );
149 - return unless $rc == 0;
150 - sleep(3);
151 - my @dirs = ();
152 - my @labels = ();
153 - foreach my $udi (qx(hal-find-by-property --key volume.fsusage --string filesystem)) {
154 - $udi =~ m/^(\S+)/;
155 -
156 - my $is_readonly = qx(hal-get-property --udi $1 --key volume.is_mounted_read_only);
157 -
158 - if ($is_readonly eq "false\n") {
159 -
160 - my $is_mounted = qx(hal-get-property --udi $1 --key volume.is_mounted);
161 -
162 - if ($is_mounted eq "false\n") {
163 - my $blkdev = qx(hal-get-property --udi $1 --key block.device);
164 - $blkdev =~ m/^(\S+)/;
165 - push @dirs, $1;
166 - }
167 - if ($is_mounted eq "false\n") {
168 - my $vollbl = qx(hal-get-property --udi $1 --key volume.label);
169 - $vollbl =~ m/^(\S+)/;
170 - if ($vollbl =~ /^\s/) {$vollbl = 'nolabel';}
171 - chomp $vollbl;
172 - push @labels, lc($vollbl);
173 - }
174 - }
175 - }
176 - unless ($dirs[0])
177 - {
178 - ($rc, $choice) = $console->message_page
179 - (
180 - title => gettext("Writable backup medium not found"),
181 - right => gettext("Back"),
182 - text =>
183 - gettext("No removable and/or writable media or device found"),
184 - );
185 - goto INITIATE_BACKUP;
186 - }
187 - mkdir("/mnt/bootstrap-console-backup");
188 -
189 - my $device = $dirs[0];
190 - if (defined $dirs[1])
191 - {
192 - my $count=1;
193 - my @args = map { $count++ . '.' => $_ } @dirs;
194 -
195 - my ($rc, $choice) = $console->menu_page
196 - (
197 - title => gettext("Choose device to use for backup"),
198 - text => ("@dirs \n @labels"),
199 - argsref => \@args,
200 - left => gettext("Cancel"),
201 - right => gettext("OK"),
202 - );
203 - goto INITIATE_BACKUP unless ($rc == 0);
204 - my %args_hash = ( @args );
205 - $device = $args_hash{$choice};
206 - }
207 - unless ( system("/bin/mount", "$device", "/mnt/bootstrap-console-backup") == '0' )
208 - {
209 - ($rc, $choice) = $console->message_page
210 - (
211 - title => gettext("No mountable backup medium"),
212 - right => gettext("Back"),
213 - text =>
214 - gettext("Unable to mount removable media, please check the file system format (default : Vfat,Ext2,Ext3,Ext4)"),
215 - );
216 - goto INITIATE_BACKUP;
217 - }
218 -
219 - use File::stat;
220 - my $st = stat("/mnt/bootstrap-console-backup/smeserver.tgz");
221 - if ($st)
222 - {
223 -# TODO
224 -# old backup exists - what do we want to do with it?
225 - my $size = $st->size;
226 - }
227 -
228 $console->infobox(
229 title => gettext("Preparing for backup"),
230 text => gettext("Please stand by while the system is prepared for backup..."),
231 );
232
233 - my $backup_size = 0;
234 system("/sbin/e-smith/signal-event", "pre-backup");
235 +
236 + $console->gauge(make_backup_callback($mountpoint,$compressionLevel), 'title' => gettext('Creating backup file'));
237 +
238 + $devices->destroy;
239 +
240 + system("/sbin/e-smith/signal-event", 'post-backup');
241 + $console->message_page
242 + (
243 + title => gettext('Backup complete'),
244 + text => gettext('Remove backup media.'),
245 + );
246 + return;
247 +}
248 +
249 +
250 +sub backupSize
251 +{
252 + my $size;
253 +
254 unless (open(DU, "-|"))
255 {
256 open(STDERR, ">/dev/null");
257 - exec qw(/usr/bin/du -sb), map { "/$_" } @backup_list;
258 + exec qw(/usr/bin/du -sb), map { "/$_" } @_;
259 }
260 while (<DU>)
261 {
262 next unless (/^(\d+)/);
263 - $backup_size += $1;
264 + $size += $1;
265 }
266 close DU;
267 -
268 - $console->gauge(make_backup_callback("/mnt/bootstrap-console-backup",$compressionLevel), 'title' => gettext("Creating backup file"));
269 -
270 - system("/bin/umount", "/mnt/bootstrap-console-backup");
271 - rmdir("/mnt/bootstrap-console-backup");
272 - system("/sbin/e-smith/signal-event", "post-backup");
273 - ($rc, $choice) = $console->message_page
274 - (
275 - title => gettext("Backup complete"),
276 - text =>
277 - gettext("Remove removable media, then hit the enter key."),
278 - );
279 +
280 + return $size;
281 }
282
283 #use esmith::console;
284 --- e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_restore.pm.devdetection 2017-02-17 14:37:15.000000000 -0500
285 +++ e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_restore.pm 2017-02-20 17:25:38.438280203 -0500
286 @@ -4,6 +4,12 @@
287 use esmith::ConfigDB;
288 use esmith::console;
289 use Locale::gettext;
290 +use Carp;
291 +use feature qw( say );
292 +use esmith::BlockDevices;
293 +use Taint::Util;
294 +
295 +my $EMPTY = q{};
296
297 sub new
298 {
299 @@ -18,7 +24,6 @@
300 return $self;
301 }
302
303 -
304 sub name
305 {
306 return $_[0]->{name};
307 @@ -32,137 +37,164 @@
308 sub doit
309 {
310 my ($self, $console, $db) = @_;
311 + my $compressionLevel = $db->get_prop('backupconsole', 'CompressionLevel') || '-6';
312 + my $mountpoint = $db->get_prop('backupconsole', 'Mountpoint') || '/mnt/bootstrap-console-backup';
313 + my $allowMounted = $db->get_prop('backupconsole', 'AllowMounted') || 'disabled'; ### For future use
314 + my $restoreMaxDepth = $db->get_prop('backupconsole', 'MaxDepth') || 1; ### For future use
315 + my %found;
316 + my $backupcount = 0;
317 + my $backupdrive; # Which filesystem holds the backup
318 + my $backupfile; # full path to the backup
319 + my ($time, $size); # time and size of chosen backup
320 +
321 return if (($db->get_prop('bootstrap-console', 'Run') eq 'no') && $self->{bootstrap} ); # regular reboot
322 if ($db->get_prop('bootstrap-console', 'Run') eq 'yes') # called from bootstrap console
323 {
324 return if ($db->get_value('PasswordSet') eq 'yes'); # too late
325 }
326 return if ($db->get_prop('bootstrap-console', 'Restore') eq 'disabled');
327 +
328 + my $devices = esmith::BlockDevices->new ('mount' => $mountpoint, 'allowmount' => $allowMounted);
329 +
330 + INITIATE_RESTORE:
331 my ($rc, $choice) = $console->yesno_page
332 (
333 title => gettext("Restore From Backup"),
334 defaultno => 1,
335 - text =>
336 - gettext("Do you wish to restore from backup?"),
337 - );
338 - return unless $rc == 0;
339 - mkdir("/mnt/bootstrap-console-backup");
340 - system("/etc/init.d/messagebus", "start");
341 - system("/etc/init.d/haldaemon", "start");
342 - INITIATE_RESTORE:
343 - ($rc, $choice) = $console->yesno_page
344 - (
345 - title => gettext("Insert media containing backup"),
346 - left => gettext("Next"),
347 - right => gettext("Cancel"),
348 - text =>
349 - gettext("Insert removable media containing your backup file, then hit the enter key."),
350 - );
351 - unless ($rc == 0) {
352 - system("/etc/init.d/haldaemon", "stop");
353 - system("/etc/init.d/messagebus", "stop");
354 - rmdir("/mnt/bootstrap-console-backup");
355 + text => gettext('Do you wish to restore from backup?')."\n\n".
356 + gettext('Insert removable media before proceeding.')."\n".
357 + gettext('It may take many seconds to scan for media.'),
358 + ); # Buttons are Yes & No
359 + if ($rc != 0) # choice was not Yes
360 + {
361 + $devices->destroy;
362 return;
363 }
364 - sleep(3);
365 - my @dirs;
366 - @dirs = ();
367 - foreach my $udi (qx(hal-find-by-property --key volume.fsusage --string filesystem)) {
368 - $udi =~ m/^(\S+)/;
369 - my $is_mounted = qx(hal-get-property --udi $1 --key volume.is_mounted);
370 -
371 - if ($is_mounted eq "false\n") {
372 - my $blkdev = qx(hal-get-property --udi $1 --key block.device);
373 - $blkdev =~ m/^(\S+)/;
374 - push @dirs, $1;
375 + ### determine which filesystems are valid or not for backups
376 +
377 + # validate each filesystem
378 + my ($valid, $invalid) = $devices->checkBackupDrives ($EMPTY);
379 + my $text = $EMPTY;
380 +
381 + if (${$valid}[0]) # There are filesystems that could hold a backup.
382 + {
383 + $text .= gettext ('These filesystems could hold backups')."\n";
384 + foreach my $drive (sort @{$valid})
385 + {
386 + $text .= "$drive ".$devices->desc($drive)."\n";
387 + $devices->findBackup ($drive, \%found, $restoreMaxDepth, \$backupcount);
388 }
389 + $text .= "\n";
390 }
391 - unless ($dirs[0])
392 - {
393 - ($rc, $choice) = $console->message_page
394 - (
395 - title => gettext("Backup medium not found"),
396 - right => "Try again",
397 - text =>
398 - gettext("No removable media or device found"),
399 - );
400 - goto INITIATE_RESTORE;
401 - }
402 - my $device = $dirs[0];
403 - if (defined $dirs[1])
404 - {
405 - my $count=1;
406 - # FIXME use better regexp
407 - my @args = map { /(.*)/; $count++ . '.' => $1 } @dirs;
408
409 - my ($rc, $choice) = $console->menu_page
410 - (
411 - title => gettext("Choose device to restore from"),
412 - text => gettext("Please select which device contains the backup file you wish to restore from."),
413 - argsref => \@args,
414 - left => gettext("Cancel"),
415 - right => gettext("OK"),
416 - );
417 + unless ($backupcount) # Unless a valid backup is found show error page
418 + {
419 + if (${$invalid}[0]) # If there are filesystems that are not valid.
420 + {
421 + $text .= gettext ('These filesystems are not valid:')."\n";
422 + foreach my $drive (sort @{$invalid})
423 + {
424 + $text .= "$drive ".$devices->desc($drive).' '.gettext ('Reason').': '.$devices->reason($drive)."\n";
425 + }
426 + $text .= "\n";
427 + }
428 + my $title = gettext('No valid backup device found');
429 + $text .= "\n$title, ".gettext('please try again');
430 + ($rc, $choice) = $console->yesno_page
431 + (
432 + title => $title,
433 + text => $text,
434 + left => gettext('Try again'),
435 + right => gettext('Cancel'),
436 + );
437 + if ($rc == 0) # Try Again
438 + {
439 + goto INITIATE_RESTORE;
440 + }
441 + else
442 + {
443 + $devices->destroy;
444 + return;
445 + }
446 + }
447 + # %found contains $backupcount backups.
448 + if ($backupcount == 1)
449 + {
450 + # One backup found, so simple yes/no choice
451 + $backupdrive = $found{1}{device}; # Find the (only) device that a backup was found on
452 + $backupfile = $found{1}{path}; # find the actual backup
453 + $time = gettext('Date') .' '. $found{1}{time};
454 + $size = gettext('Size') .' '. $found{1}{sizeH};
455 +
456 + ($rc, $choice) = $console->yesno_page
457 + (
458 + title => gettext('Start restore from backup'),
459 + text =>
460 + gettext('Backup found on device').
461 + "\n$backupdrive ".$devices->desc($backupdrive)."\n\n".
462 + gettext('Backup details').
463 + "\n$backupfile $size $time\n\n\n".
464 + gettext('Do you wish to restore from this file?'),
465 + );
466 goto INITIATE_RESTORE unless ($rc == 0);
467 - my %args_hash = ( @args );
468 - $device = $args_hash{$choice};
469 + $size = $found{1}{size};
470 }
471 - system("/bin/mount", "$device", "/mnt/bootstrap-console-backup");
472 - sleep(1);
473 -
474 - unless (-f "/mnt/bootstrap-console-backup/smeserver.tgz")
475 + else # Multiple backups found so display a choice
476 {
477 - system("/bin/umount", "$device");
478 - ($rc, $choice) = $console->message_page
479 - (
480 - title => gettext("Backup file not found"),
481 - right => "Try again",
482 - text =>
483 - gettext("No backup file found"),
484 - );
485 - goto INITIATE_RESTORE;
486 - }
487 - use File::stat;
488 - my $st = stat("/mnt/bootstrap-console-backup/smeserver.tgz");
489 - my $size = $st->size;
490 -
491 - ($rc, $choice) = $console->yesno_page
492 - (
493 - title => gettext("Start restore from backup"),
494 - text =>
495 - gettext("Backup file found:") . " smeserver.tgz ($device) " .
496 - gettext("size") . " $size " . gettext("bytes") .
497 - "\n\n" .
498 - gettext("Do you wish to restore from this file?"),
499 - );
500 - unless ($rc == 0) {
501 - system("/bin/umount", "$device");
502 - goto INITIATE_RESTORE;
503 + $text = gettext ('Backups found on these devices')."\n";
504 + foreach my $backupfound (sort keys %found)
505 + {
506 + $backupdrive = $found{$backupfound}{device};
507 + if (($backupfound == 1) || ($found{$backupfound}{device} ne $found{$backupfound-1}{device}))
508 + {
509 + $text.= "$backupdrive ".$devices->desc($backupdrive)."\n";
510 + }
511 + }
512 + my @args = map { $_ => "$found{$_}{device} $found{$_}{path} $found{$_}{sizeH} $found{$_}{time}" } sort keys %found;
513 + ($rc, $choice) = $console->menu_page
514 + (
515 + title => gettext('Start restore from backup'),
516 + text =>
517 + "$text\n".
518 + gettext ('Please select the backup that you wish to restore from.'),
519 + argsref => \@args,
520 + left => gettext('Cancel'),
521 + right => gettext('OK'),
522 + );
523 + goto INITIATE_RESTORE unless ($rc == 0);
524 + untaint $choice;
525 + $backupdrive = $found{$choice}{device};
526 + $size = $found{$choice}{size};
527 }
528 +
529 + $devices->mount ($backupdrive); # mount the chosen filesystem
530 + sleep(1); # Some mounts take time to become active
531 +
532 + ###ToDo This section has no error checking
533 + ###ToDo 'Restoring data is not localized
534 + # Execute the restore
535 system("/sbin/e-smith/signal-event", "pre-restore");
536 - system("(cd / ; cat /mnt/bootstrap-console-backup/smeserver.tgz |
537 + system("(cd / ; cat $mountpoint/smeserver.tgz |
538 pv -n -s $size |
539 gunzip |
540 tar xf - > /dev/null ) 2>&1 |
541 dialog --backtitle 'Restoring data' --guage 'Progress' 7 70");
542 - system("/bin/umount", "$device");
543 - system("/etc/init.d/haldaemon", "stop");
544 - system("/etc/init.d/messagebus", "stop");
545 - rmdir("/mnt/bootstrap-console-backup");
546 - system("/sbin/e-smith/signal-event", "post-upgrade");
547 -
548 +
549 + # Restore complete, now clean-up
550 + $devices->destroy;
551 + system("/sbin/e-smith/signal-event", "post-upgrade");
552 unless ( $self->{bootstrap} )
553 {
554 - $db->set_prop("bootstrap-console", "Run", "yes");
555 + $db->set_prop("bootstrap-console", "Run", "yes");
556 $db->set_prop("bootstrap-console", "ForceSave", "yes");
557 $db->set_prop("bootstrap-console", "Restore", "disabled");
558 -
559 - system("/usr/bin/tput", "clear");
560 - system("/sbin/e-smith/signal-event", "reboot");
561 -
562 - # A bit of a hack to avoid the console restarting before the
563 - # reboot takes effect.
564 -
565 +
566 + system("/usr/bin/tput", "clear");
567 + system("/sbin/e-smith/signal-event", "reboot");
568 +
569 + # A bit of a hack to avoid the console restarting before the
570 + # reboot takes effect.
571 +
572 sleep(600);
573 }
574 return;
575 --- e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/BlockDevices.pm.devdetection 2017-02-24 14:39:00.743982759 -0500
576 +++ e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/BlockDevices.pm 2017-02-24 14:47:46.334675319 -0500
577 @@ -0,0 +1,503 @@
578 +#----------------------------------------------------------------------
579 +# Copyright 2015 Ian Wells
580 +# This program is free software; you can redistribute it and/or
581 +# modify it under the same terms as Perl itself.
582 +#----------------------------------------------------------------------
583 +
584 +package esmith::BlockDevices;
585 +
586 +use strict;
587 +use warnings;
588 +use English '-no_match_vars';
589 +use Carp;
590 +use File::Path qw(make_path remove_tree);
591 +use POSIX qw(:sys_wait_h strftime);
592 +use Locale::gettext;
593 +use File::stat;
594 +use v5.10.1;
595 +use Taint::Util;
596 +use Readonly;
597 +use File::Find;
598 +
599 +use vars qw($VERSION @ISA @EXPORT_OK);
600 +
601 +@ISA = qw(Exporter);
602 +
603 +=head1 NAME
604 +
605 +esmith::BlockDevices - Module to handle block devices
606 +
607 +=head1 SYNOPSIS
608 +
609 + use esmith::BlockDevices;
610 + my $devices = BlockDevices->new ();
611 +
612 +=head1 DESCRIPTION
613 +
614 +This module provides an abstracted interface to the
615 +block devices used for backup/restore
616 +
617 +=cut
618 +
619 +my $EMPTY = q{};
620 +
621 +sub new
622 +{
623 + my $class = shift;
624 + my $self = {
625 + _blox => lsblk(),
626 + mount => findValidFS(),
627 + _fstype => $EMPTY,
628 + allowmount => $EMPTY,
629 + @_,
630 + };
631 + bless $self, $class;
632 + return $self;
633 +}
634 +
635 +sub lsblk
636 +{
637 +#ToDo add some comments
638 + my %blox; # a hash to hold the device information
639 +
640 + my $short = qx(/bin/lsblk -sdn -o KNAME);
641 + my @long = qx(/bin/lsblk -P -b -o KNAME,MAJ:MIN,RM,RO,TYPE,MOUNTPOINT,FSTYPE,LABEL,UUID,MODEL,SIZE,STATE,MODE,OWNER,GROUP);
642 + # Not all of this information may be needed currently, but it does not affect the processing time
643 + untaint ($short);
644 + untaint (@long);
645 +
646 + my $devicemodel= $EMPTY;
647 +
648 + for (@long)
649 + {
650 + my @line = split /\"\s/s;
651 + my $name;
652 + if ($line[0] =~ /KNAME=\"(.*)/s)
653 + {
654 + $name = $1;
655 + }
656 + else {carp 'Could not match KNAME'; last;} # should never occur.
657 +
658 + $blox{$name}{tip} = ($short =~ m/^$name$/sm) ? 1 : 0;
659 +
660 + for (@line)
661 + {
662 + my ($key,$value) = split /=/s;
663 + $value =~ s/\"//sg;
664 + $blox{$name}{$key} = trim($value);
665 + }
666 + if ($blox{$name}{TYPE} =~ /rom|disk/s)
667 + {
668 + $devicemodel = $blox{$name}{MODEL};
669 + }
670 + else
671 + {
672 + $blox{$name}{MODEL} = trim($devicemodel);
673 + }
674 + $blox{$name}{SIZEH} = scaleIt($blox{$name}{SIZE});
675 + }
676 + return \%blox;
677 +}
678 +
679 +sub findValidFS
680 +{
681 +# Find all filesystem types that are supported
682 + my %fs; # a hash to hold the supported filesystem type information
683 +
684 + my @cmd = `cat /proc/filesystems`;
685 + foreach (@cmd)
686 + {
687 + if (/.*\t(.*?)$/s){$fs {$1}=$1;}
688 + }
689 + @cmd = `ls -1 /lib/modules/\$(uname -r)/kernel/fs/*/*ko`;
690 + foreach (@cmd)
691 + {
692 + if (/.*\/(.*?)\.ko/s){$fs {$1}=$1;}
693 + }
694 + return \%fs;
695 +}
696 +
697 +sub scanBlocks
698 +{
699 + # Scan all the block devices
700 + # This takes some seconds on systems with many filesystems
701 + my ($self) = @_;
702 + $self->{_blox} = lsblk;
703 + $self->{_fstype} = findValidFS;
704 + return;
705 +}
706 +
707 +sub list
708 +{
709 + my ($self) = @_;
710 + my @dirs=();
711 + my $hashref = $self->{_blox};
712 +
713 + foreach my $drive (keys %{$hashref})
714 + {
715 + push @dirs, $drive;
716 + }
717 +
718 + return @dirs;
719 +}
720 +
721 +sub checkBackupDriveSize
722 +{
723 + my ($self,$drive, $size) = @_;
724 + my $hashref = $self->{_blox};
725 + my $sz = $EMPTY;
726 + my $mntdir = $self->{mount};
727 + Readonly my $VFAT_LIMIT => 2147483648;
728 + Readonly my $KBYTE => 1024;
729 +
730 + # size > drive size
731 + if ($size > $hashref->{$drive}{SIZE})
732 + {
733 + return 1; # filesystem too small
734 + }
735 +
736 + # FAT32 drive and size > 2G
737 + if (($size > $VFAT_LIMIT) && ($hashref->{$drive}{FSTYPE} eq 'vfat'))
738 + {
739 + return 2; # filesystem vfat limit
740 + }
741 +
742 +#ToDo add a check here to see if mounting is allowed by db value
743 +
744 + # check mount and find actual size
745 + if ($self->mountable ($drive)) # Only check filesystems that appear mountable
746 + {
747 + $self->mount ($drive);
748 + my $filesize = -s "$mntdir/smeserver.tgz";
749 +
750 + # Check free disk space
751 + my $df = qx(/usr/bin/df -P \"$mntdir\");
752 + if ($df =~ /(\S+)\s+(\S+)\s+(\S+)\s+(\d*%)/s)
753 + {
754 + my $dsize = ($3 * $KBYTE) + ($filesize //= 0);
755 + if ($size > $dsize) # not enough space
756 + {
757 + $sz = 3; # filesystem has too little free space
758 + }
759 + }
760 + else # fail (never seen in testing)
761 + {
762 + $sz = 4; # Failed to get disk size
763 + }
764 + $self->unmount;
765 + }
766 + return $sz;
767 +}
768 +
769 +# Check each block device
770 +# Return two arrays, valid drives, invalid drives
771 +sub checkBackupDrives
772 +{
773 + my ($self,$bsize) = @_;
774 + my @valid = ();
775 + my @invalid = ();
776 + $self->scanBlocks; # scan all block devices
777 + my $hashref = $self->{_blox};
778 + my $allowmount = $self->{'allowmount'}; # Are mounted drives allowed in $checks.
779 + my $checks = 'UU RO FS'; # These checks are always valid
780 + $checks .= ' MO' if ($allowmount eq 'enabled');
781 + $checks .= ' SZ' if ($bsize); # Only run the size check when a valid size is given
782 +
783 + foreach my $drive (keys %{$hashref})
784 + {
785 + $hashref->{$drive}{REASON} = $EMPTY; # Reason for a filesystem being (in)valid
786 + next unless $hashref->{$drive}{tip}; #Ignore drives that have child filesystems
787 +
788 + # drives mounted on /, /boot, or [SWAP] are never valid for backups
789 + next if ($hashref->{$drive}{MOUNTPOINT} =~ /^\/boot$|^\[SWAP\]$|^\/$/s );
790 +
791 + # validate each filesystem against the checks
792 + foreach my $check (split / /s, $checks)
793 + {
794 + for ($check)
795 + {
796 + if (/^UU/si) # No UUID
797 + {
798 + $hashref->{$drive}{REASON} .='UU ' unless $self->uuid ($drive); last;
799 + }
800 + if (/^RO/si) # Read Only
801 + {
802 + $hashref->{$drive}{REASON} .='RO ' if $self->readonly ($drive); last;
803 + }
804 + if (/^FS/si) # Invalid filesystem
805 + {
806 + $hashref->{$drive}{REASON} .='FS ' unless $self->validFS ($drive); last;
807 + }
808 + if (/^MO/si) # Mounted
809 + {
810 + $hashref->{$drive}{REASON} .='MO ' if $self->mountpoint ($drive); last;
811 + }
812 + if (/^SZ/si) # filesystem size, this includes mounting to check free space
813 + {
814 + $hashref->{$drive}{REASON} .='SZ ' if $self->checkBackupDriveSize ($drive, $bsize);
815 + #ToDo the return value contains the reason why there is insufficient space, but this is not used yet.
816 + last;
817 + }
818 + { carp "not supported yet in checkBackupDrives: $check"; } # Should never be seen
819 + }
820 + }
821 + if ($hashref->{$drive}{REASON})
822 + {
823 + push @invalid, $drive;
824 + }
825 + else
826 + {
827 + push @valid, $drive;
828 + }
829 + }
830 + return (\@valid, \@invalid);
831 +}
832 +
833 +sub findBackup
834 +{
835 + my ($self, $kname, $foundref, $maxDepth, $count) = @_;
836 + my $hashref = $self->{_blox};
837 + my $mountpoint = $self->{'mount'};
838 + my $file = 'smeserver.tgz';
839 +
840 + $self->mount ($kname);
841 + sleep 1;
842 +
843 + # start with the absolute path
844 + my $findRoot = Cwd::realpath($mountpoint);
845 +
846 + # determine the depth of our beginning directory
847 + my $begDepth = 1 + grep { length } File::Spec->splitdir($findRoot);
848 +
849 + find (
850 + {
851 + preprocess => sub
852 + { @_ if (scalar File::Spec->splitdir($File::Find::dir) - $begDepth) <= $maxDepth },
853 + wanted => sub
854 + {
855 + if (($_ =~ m/^$file/s) && ($File::Find::name =~ qr|^([-+@\w\s./:\\]+)$| )) # if matching the backup name
856 + {
857 + $$count++;
858 + my $sb = stat $1;
859 + ${$foundref}{$$count}{count}=$$count;
860 + ${$foundref}{$$count}{device}=$kname;
861 + ${$foundref}{$$count}{path} = $1;
862 + ${$foundref}{$$count}{path} =~ s/$mountpoint//; #strip off the mountpoint
863 + ${$foundref}{$$count}{path} =~ s/$file//; #strip off the filename
864 + ${$foundref}{$$count}{size}=$sb->size; # size in bytes
865 + ${$foundref}{$$count}{sizeH}=scaleIt($sb->size); # human readable size
866 + ${$foundref}{$$count}{time}=strftime '%d %b %g %H:%M', localtime $sb->mtime;
867 + }
868 + },
869 + untaint => 1,
870 + untaint_pattern => qr|^([-+@\w\s./:\\]+)$|,
871 + untaint_skip =>1,
872 + },
873 + $findRoot
874 + );
875 +
876 + $self->unmount;
877 + return;
878 +}
879 +
880 +sub desc # brief description of a filesystem
881 +{
882 + my ($self,$kname) = @_;
883 + my $hashref = $self->{_blox};
884 +
885 + my $model = $hashref->{$kname}{MODEL};
886 + my $label = $hashref->{$kname}{LABEL} || gettext('no label');
887 + my $size = $hashref->{$kname}{SIZEH};
888 +
889 + return "$label $model $size";
890 +}
891 +
892 +
893 +# Given the KNAME check if the filesystem.could be mountable
894 +# Check that there are no children, i.e. a tip
895 +# Check that it has a UUID, Filesystem,
896 +sub mountable
897 +{
898 + my ($self,$kname) = @_;
899 + my $hashref = $self->{_blox};
900 + return ($hashref->{$kname}{tip} && $hashref->{$kname}{UUID} && _isFS ($hashref->{$kname}{FSTYPE})) ? 1 : $EMPTY;
901 +}
902 +
903 +# Given the KNAME check if the filesystem.is read-only
904 +# returns 1 for Read-Only and $EMPTY for R-W
905 +sub readonly
906 +{
907 + my ($self,$kname) = @_;
908 + my $hashref = $self->{_blox};
909 + return ($hashref->{$kname}{RO}) ? 1 : $EMPTY;
910 +}
911 +
912 +sub mountpoint
913 +{
914 + my ($self,$kname) = @_;
915 + my $hashref = $self->{_blox};
916 + return ($hashref->{$kname}{MOUNTPOINT});
917 +}
918 +
919 +sub uuid
920 +{
921 + my ($self,$kname) = @_;
922 + my $hashref = $self->{_blox};
923 + return ($hashref->{$kname}{UUID});
924 +}
925 +
926 +sub model
927 +{
928 + my ($self,$kname) = @_;
929 + my $hashref = $self->{_blox};
930 + return ($hashref->{$kname}{MODEL});
931 +}
932 +
933 +# Given the KNAME return the label
934 +# returns 'no label' if none found
935 +sub label
936 +{
937 + my ($self,$kname) = @_;
938 + my $hashref = $self->{_blox};
939 + return ($hashref->{$kname}{LABEL}) || gettext('no label');
940 +}
941 +
942 +sub size
943 +{
944 + my ($self,$kname) = @_;
945 + my $hashref = $self->{_blox};
946 + return ($hashref->{$kname}{SIZE});
947 +}
948 +
949 +# Given a filesystem.(eg sr0) check if it's filesystem type is allowed
950 +sub validFS
951 +{
952 + my ($self,$kname) = @_;
953 + my $hashref = $self->{_blox};
954 + my $fsref = $self->{_fstype};
955 + return ($fsref->{$hashref->{$kname}{FSTYPE}}) || $EMPTY;
956 +}
957 +
958 +# Given a filesystem.type (eg vfat) check if it is allowed
959 +sub _isFS
960 +{
961 + my ($filesystem) = @_;
962 + return $EMPTY unless $filesystem;
963 +
964 + my $fsref = findValidFS;
965 + return ($fsref->{$filesystem}) || $EMPTY;
966 +}
967 +
968 +# Return the reason string which indicates why a drive is (in)valid
969 +sub reason
970 +{
971 + my ($self,$kname) = @_;
972 + my $hashref = $self->{_blox};
973 + return ($hashref->{$kname}{REASON});
974 +}
975 +
976 +# Given the KNAME mount the filesystem, example
977 +# system ('/bin/mount', '-t', 'vfat', '-U', '9891-4C8A', '/tmp/mnt');
978 +sub mount
979 +{
980 + my ($self, $kname) = @_;
981 + my $hashref = $self->{_blox};
982 +
983 + $self->createMountpoint;
984 +
985 + system ('/bin/mount', '-t', $hashref->{$kname}{FSTYPE}, '-U', $hashref->{$kname}{UUID}, $self->{mount}) == 0
986 + or croak (gettext('Failed to mount')." $self->{mount},$hashref->{$kname}{FSTYPE},$hashref->{$kname}{UUID}: $?");
987 + return;
988 +}
989 +
990 +# Unmount the block device
991 +sub unmount
992 +{
993 + my $self = shift;
994 + system('/bin/umount', $self->{mount}) == 0
995 + or croak (gettext('Failed to unmount')." $self->{mount}: $?");
996 + return;
997 +}
998 +
999 +# Create the mountpoint directory
1000 +# Error if already mounted
1001 +sub createMountpoint
1002 +{
1003 + my $self = shift;
1004 + my $mount = $self->{mount};
1005 +
1006 + # Check if the mountpoint is in use
1007 + if (!checkMount ($mount))
1008 + {
1009 + # Try to unmount, will die if fails
1010 + $self->unmount;
1011 + }
1012 +
1013 + if ($mount && ! -d $mount)
1014 + {
1015 + eval {make_path($mount)};
1016 + croak (gettext('Error while creating')." $mount $EVAL_ERROR".gettext('Maybe insufficient permissions.')) if $EVAL_ERROR;
1017 + }
1018 + return;
1019 +}
1020 +
1021 +sub destroy
1022 +{
1023 +# cleanup, unmount and remove mountpoint
1024 +
1025 + my $self = shift;
1026 + my $mount = $self->{mount};
1027 +
1028 +
1029 + # Check if the mountpoint is in use
1030 + if (!checkMount ($mount))
1031 + {
1032 + $self->unmount;
1033 + }
1034 +
1035 + if ($mount && -d $mount)
1036 + {
1037 + eval {remove_tree($mount)};
1038 + croak (gettext('Error while deleting')." $mount $EVAL_ERROR") if $EVAL_ERROR;
1039 + }
1040 + return;
1041 +}
1042 +
1043 +
1044 +### The following subroutines are not specific to block devices
1045 +sub scaleIt {
1046 + Readonly my $KBYTE => 1024;
1047 + my( $size, $n ) =( shift, 0 );
1048 + ++$n and $size /= $KBYTE until $size < $KBYTE;
1049 + if ($size >= 1000){++$n ; $size /= $KBYTE;}
1050 + return sprintf "%.3g %s",
1051 + $size, ( qw[ bytes KB MB GB TB] )[ $n ];
1052 +}
1053 +
1054 +sub checkMount
1055 +{
1056 + # check if $mountdir is mounted
1057 + my $mountdir = shift;
1058 + $|=1; # Auto-flush
1059 +
1060 + # copy STDOUT to another filehandle
1061 + open (my $STDOLD, '>&', STDOUT);
1062 +
1063 + open(STDOUT, ">/dev/null");
1064 + if ( open(MOUNTDIR, "|-", "/bin/findmnt", $mountdir)){;}
1065 +
1066 + # restore STDOUT
1067 + open (STDOUT, '>&', $STDOLD);
1068 +
1069 + return (!close(MOUNTDIR));
1070 +}
1071 +
1072 +# remove leading and trailing spaces from a string
1073 +# this should be moved to a util library.
1074 +sub trim
1075 +{
1076 + my ($string) = @_;
1077 + $string =~ s/^\s+|\s+$//g;
1078 + return $string;
1079 +}
1080 +1;

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed