]> git.ipfire.org Git - ipfire-3.x.git/blob - src/pomona/fsset.py
Added some lines for ext4 on installer.
[ipfire-3.x.git] / src / pomona / fsset.py
1 #
2 # fsset.py: filesystem management
3 #
4 # Matt Wilson <msw@redhat.com>
5 #
6 # Copyright 2001-2006 Red Hat, Inc.
7 #
8 # This software may be freely redistributed under the terms of the GNU
9 # library public license.
10 #
11 # You should have received a copy of the GNU Library Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
14 #
15
16 import string
17 import isys
18 import inutil
19 import os
20 import resource
21 import posix
22 import stat
23 import errno
24 import parted
25 import sys
26 import struct
27 import time
28 import partitioning
29 import partedUtils
30 import types
31 import math
32 from flags import flags
33
34 import gettext
35 _ = lambda x: gettext.ldgettext("pomona", x)
36 N_ = lambda x: x
37
38 import logging
39 log = logging.getLogger("pomona")
40
41 class BadBlocksError(Exception):
42 pass
43
44 class SuspendError(Exception):
45 pass
46
47 class OldSwapError(Exception):
48 pass
49
50 class ResizeError(Exception):
51 pass
52
53 defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr', '/var', '/opt']
54
55 fileSystemTypes = {}
56
57 def fileSystemTypeGetDefault():
58 if fileSystemTypeGet('ext4').isSupported():
59 return fileSystemTypeGet('ext4')
60 elif fileSystemTypeGet('ext3').isSupported():
61 return fileSystemTypeGet('ext3')
62 elif fileSystemTypeGet('ext2').isSupported():
63 return fileSystemTypeGet('ext2')
64 else:
65 raise ValueError, "You have neither ext4, ext3 or ext2 support in your kernel!"
66
67 def fileSystemTypeGet(key):
68 return fileSystemTypes[key]
69
70 def fileSystemTypeRegister(klass):
71 fileSystemTypes[klass.getName()] = klass
72
73 def fileSystemTypeGetTypes():
74 return fileSystemTypes.copy()
75
76 def getUsableLinuxFs():
77 rc = []
78 for fsType in fileSystemTypes.keys():
79 if fileSystemTypes[fsType].isMountable() and \
80 fileSystemTypes[fsType].isLinuxNativeFS():
81 rc.append(fsType)
82
83 # make sure the default is first in the list, kind of ugly
84 default = fileSystemTypeGetDefault()
85 defaultName = default.getName()
86 if defaultName in rc:
87 del rc[rc.index(defaultName)]
88 rc.insert(0, defaultName)
89 return rc
90
91 def devify(device):
92 if device in ["proc", "devpts", "sysfs", "tmpfs"]:
93 return device
94 elif device == "sys":
95 return "sysfs"
96 elif device == "shm":
97 return "tmpfs"
98 elif device != "none" and device[0] != '/':
99 return "/dev/" + device
100 else:
101 return device
102
103 class LabelFactory:
104 def __init__(self):
105 self.labels = None
106
107 def createLabel(self, mountpoint, maxLabelChars):
108 if self.labels == None:
109 self.labels = {}
110 diskset = partedUtils.DiskSet()
111 diskset.openDevices()
112 labels = diskset.getLabels()
113 del diskset
114 self.reserveLabels(labels)
115
116 if len(mountpoint) > maxLabelChars:
117 mountpoint = mountpoint[0:maxLabelChars]
118 count = 0
119 while self.labels.has_key(mountpoint):
120 count = count + 1
121 s = "%s" % count
122 if (len(mountpoint) + len(s)) <= maxLabelChars:
123 mountpoint = mountpoint + s
124 else:
125 strip = len(mountpoint) + len(s) - maxLabelChars
126 mountpoint = mountpoint[0:len(mountpoint) - strip] + s
127 self.labels[mountpoint] = 1
128
129 return mountpoint
130
131 def reserveLabels(self, labels):
132 if self.labels == None:
133 self.labels = {}
134 for device, label in labels.items():
135 self.labels[label] = 1
136
137 def isLabelReserved(self, label):
138 if self.labels == None:
139 return False
140 elif self.labels.has_key(label):
141 return True
142 else:
143 return False
144
145 labelFactory = LabelFactory()
146
147 class FileSystemType:
148 kernelFilesystems = {}
149 lostAndFoundContext = None
150
151 def __init__(self):
152 self.deviceArguments = {}
153 self.formattable = 0
154 self.checked = 0
155 self.name = ""
156 self.linuxnativefs = 0
157 self.partedFileSystemType = None
158 self.partedPartitionFlags = []
159 self.maxSizeMB = 8 * 1024 * 1024
160 self.supported = -1
161 self.defaultOptions = "defaults"
162 self.migratetofs = None
163 self.extraFormatArgs = []
164 self.maxLabelChars = 16
165 self.resizable = False
166 self.supportsFsProfiles = False
167 self.fsProfileSpecifier = None
168 self.fsprofile = None
169 self.bootable = False
170
171 def createLabel(self, mountpoint, maxLabelChars, kslabel = None):
172 # If a label was specified in the kickstart file, return that as the
173 # label.
174 if kslabel:
175 return kslabel
176
177 if len(mountpoint) > maxLabelChars:
178 return mountpoint[0:maxLabelChars]
179 else:
180 return mountpoint
181
182 def isBootable(self):
183 return self.bootable
184
185 def isResizable(self):
186 return self.resizable
187 def resize(self, entry, size, progress, chroot='/'):
188 pass
189 def getMinimumSize(self, device):
190 log.warning("Unable to determinine minimal size for %s", device)
191 return 1
192
193 def isKernelFS(self):
194 """Returns True if this is an in-kernel pseudo-filesystem."""
195 return False
196
197 def mount(self, device, mountpoint, readOnly=0, bindMount=0,
198 instroot=""):
199 if not self.isMountable():
200 return
201 inutil.mkdirChain("%s/%s" %(instroot, mountpoint))
202 log.debug("mounting %s on %s/%s as %s" %(device, instroot,
203 mountpoint, self.getMountName()))
204 isys.mount(device, "%s/%s" %(instroot, mountpoint),
205 fstype = self.getMountName(),
206 readOnly = readOnly, bindMount = bindMount,
207 options = self.defaultOptions)
208
209 def umount(self, device, path):
210 isys.umount(path, removeDir = 0)
211
212 def getName(self, quoted = 0):
213 """Return the name of the filesystem. Set quoted to 1 if this
214 should be quoted (ie, it's not for display)."""
215 if quoted:
216 if self.name.find(" ") != -1:
217 return "\"%s\"" %(self.name,)
218 return self.name
219
220 def getMountName(self, quoted = 0):
221 return self.getName(quoted)
222
223 def registerDeviceArgumentFunction(self, klass, function):
224 self.deviceArguments[klass] = function
225
226 def formatDevice(self, entry, progress, chroot='/'):
227 if self.isFormattable():
228 raise RuntimeError, "formatDevice method not defined"
229
230 def migrateFileSystem(self, device, message, chroot='/'):
231 if self.isMigratable():
232 raise RuntimeError, "migrateFileSystem method not defined"
233
234 def labelDevice(self, entry, chroot):
235 pass
236
237 def clobberDevice(self, entry, chroot):
238 pass
239
240 def isFormattable(self):
241 return self.formattable
242
243 def isLinuxNativeFS(self):
244 return self.linuxnativefs
245
246 def setFsProfile(self, fsprofile=None):
247 if not self.supportsFsProfiles:
248 raise RuntimeError, "%s does not support profiles" % (self,)
249 self.fsprofile = fsprofile
250
251 def getFsProfileArgs(self):
252 if not self.supportsFsProfiles:
253 raise RuntimeError, "%s does not support profiles" % (self,)
254 args = None
255 if self.fsprofile:
256 args = []
257 if self.fsProfileSpecifier:
258 args.extend(self.fsProfileSpecifier)
259 args.extend(self.fsprofile)
260 return args
261
262 def readProcFilesystems(self):
263 f = open("/proc/filesystems", 'r')
264 if not f:
265 pass
266 lines = f.readlines()
267 for line in lines:
268 fields = string.split(line)
269 if fields[0] == "nodev":
270 fsystem = fields[1]
271 else:
272 fsystem = fields[0]
273 FileSystemType.kernelFilesystems[fsystem] = None
274
275 def isMountable(self):
276 if not FileSystemType.kernelFilesystems:
277 self.readProcFilesystems()
278
279 return FileSystemType.kernelFilesystems.has_key(self.getMountName()) or self.getName() == "auto"
280
281 def isSupported(self):
282 if self.supported == -1:
283 return self.isMountable()
284 return self.supported
285
286 def isChecked(self):
287 return self.checked
288
289 def getDeviceArgs(self, device):
290 deviceArgsFunction = self.deviceArguments.get(device.__class__)
291 if not deviceArgsFunction:
292 return []
293 return deviceArgsFunction(device)
294
295 def getPartedFileSystemType(self):
296 return self.partedFileSystemType
297
298 def getPartedPartitionFlags(self):
299 return self.partedPartitionFlags
300
301 # note that this returns the maximum size of a filesystem in megabytes
302 def getMaxSizeMB(self):
303 return self.maxSizeMB
304
305 def getDefaultOptions(self, mountpoint):
306 return self.defaultOptions
307
308 def getMigratableFSTargets(self):
309 retval = []
310 if not self.migratetofs:
311 return retval
312
313 for fs in self.migratetofs:
314 if fileSystemTypeGet(fs).isSupported():
315 retval.append(fs)
316
317 return retval
318
319 def isMigratable(self):
320 if len(self.getMigratableFSTargets()) > 0:
321 return 1
322 else:
323 return 0
324
325
326 class reiserfsFileSystem(FileSystemType):
327 def __init__(self):
328 FileSystemType.__init__(self)
329 self.partedFileSystemType = parted.file_system_type_get("reiserfs")
330 self.formattable = 1
331 self.checked = 1
332 self.linuxnativefs = 1
333 self.supported = -1
334 self.name = "reiserfs"
335
336 self.maxSizeMB = 8 * 1024 * 1024
337
338 def formatDevice(self, entry, progress, chroot='/'):
339 devicePath = entry.device.setupDevice(chroot)
340 p = os.pipe()
341 os.write(p[1], "y\n")
342 os.close(p[1])
343
344 rc = inutil.execWithRedirect("mkreiserfs",
345 [devicePath],
346 stdin = p[0],
347 stdout = "/dev/tty5",
348 stderr = "/dev/tty5", searchPath = 1)
349
350 if rc:
351 raise SystemError
352
353 def labelDevice(self, entry, chroot):
354 devicePath = entry.device.setupDevice(chroot)
355 label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars)
356 rc = inutil.execWithRedirect("reiserfstune",
357 ["--label", label, devicePath],
358 stdout = "/dev/tty5",
359 stderr = "/dev/tty5", searchPath = 1)
360 if rc:
361 raise SystemError
362 entry.setLabel(label)
363
364 fileSystemTypeRegister(reiserfsFileSystem())
365
366 class xfsFileSystem(FileSystemType):
367 def __init__(self):
368 FileSystemType.__init__(self)
369 self.partedFileSystemType = parted.file_system_type_get("xfs")
370 self.formattable = 1
371 self.checked = 1
372 self.linuxnativefs = 1
373 self.name = "xfs"
374 self.maxSizeMB = 16 * 1024 * 1024
375 self.maxLabelChars = 12
376 self.supported = -1
377
378 def formatDevice(self, entry, progress, chroot='/'):
379 devicePath = entry.device.setupDevice(chroot)
380
381 rc = inutil.execWithRedirect("mkfs.xfs",
382 ["-f", "-l", "internal",
383 "-i", "attr=2", devicePath],
384 stdout = "/dev/tty5",
385 stderr = "/dev/tty5", searchPath = 1)
386
387 if rc:
388 raise SystemError
389
390 def labelDevice(self, entry, chroot):
391 devicePath = entry.device.setupDevice(chroot)
392 label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars)
393 db_cmd = "label " + label
394 rc = inutil.execWithRedirect("xfs_db",
395 ["-x", "-c", db_cmd, devicePath],
396 stdout = "/dev/tty5",
397 stderr = "/dev/tty5", searchPath = 1)
398 if rc:
399 raise SystemError
400 entry.setLabel(label)
401
402 fileSystemTypeRegister(xfsFileSystem())
403
404 class extFileSystem(FileSystemType):
405 def __init__(self):
406 FileSystemType.__init__(self)
407 self.partedFileSystemType = None
408 self.formattable = 1
409 self.checked = 1
410 self.linuxnativefs = 1
411 self.maxSizeMB = 8 * 1024 * 1024
412 self.supportsFsProfiles = True
413 self.fsProfileSpecifier = "-T"
414 self.resizable = True
415 self.bootable = True
416
417 def resize(self, entry, size, progress, chroot='/'):
418 devicePath = entry.device.setupDevice(chroot)
419
420 log.info("checking %s prior to resize" %(devicePath,))
421 w = None
422 if progress:
423 w = progress(_("Checking"),
424 _("Checking filesystem on %s...") %(devicePath),
425 100, pulse = True)
426
427 rc = inutil.execWithPulseProgress("e2fsck", ["-f", "-p", "-C", "0", devicePath],
428 stdout="/tmp/resize.out",
429 stderr="/tmp/resize.out",
430 progress = w)
431 if rc >= 4:
432 raise ResizeError, ("Check of %s failed" %(devicePath,), devicePath)
433 if progress:
434 w.pop()
435 w = progress(_("Resizing"),
436 _("Resizing filesystem on %s...") %(devicePath),
437 100, pulse = True)
438
439 log.info("resizing %s" %(devicePath,))
440 rc = inutil.execWithPulseProgress("resize2fs",
441 ["-p", devicePath, "%sM" %(size,)],
442 stdout="/tmp/resize.out",
443 stderr="/tmp/resize.out",
444 progress = w)
445 if progress:
446 w.pop()
447 if rc:
448 raise ResizeError, ("Resize of %s failed" %(devicePath,), devicePath)
449
450 def getMinimumSize(self, device):
451 """Return the minimum filesystem size in megabytes"""
452 devicePath = "/dev/%s" % (device,)
453
454 # FIXME: it'd be nice if we didn't have to parse this out ourselves
455 buf = inutil.execWithCapture("dumpe2fs",
456 ["-h", devicePath],
457 stderr = "/dev/tty5")
458 blocks = free = bs = 0
459 for l in buf.split("\n"):
460 if l.startswith("Free blocks"):
461 try:
462 free = l.split()[2]
463 free = int(free)
464 except Exception, e:
465 log.warning("error determining free blocks on %s: %s" %(devicePath, e))
466 free = 0
467 elif l.startswith("Block size"):
468 try:
469 bs = l.split()[2]
470 bs = int(bs)
471 except Exception, e:
472 log.warning("error determining block size of %s: %s" %(devicePath, e))
473 bs = 0
474 elif l.startswith("Block count"):
475 try:
476 blocks = l.split()[2]
477 blocks = int(blocks)
478 except Exception, e:
479 log.warning("error determining block count of %s: %s" %(devicePath, e))
480 blocks = 0
481
482 if free == 0 or bs == 0:
483 log.warning("Unable to determinine minimal size for %s", devicePath)
484 return 1
485
486 used = math.ceil((blocks - free) * bs / 1024.0 / 1024.0)
487 log.info("used size of %s is %s" %(devicePath, used))
488 # FIXME: should we bump this beyond the absolute minimum?
489 return used
490
491 def labelDevice(self, entry, chroot):
492 devicePath = entry.device.setupDevice(chroot)
493 label = self.createLabel(entry.mountpoint, self.maxLabelChars,
494 kslabel = entry.label)
495
496 rc = inutil.execWithRedirect("e2label",
497 [devicePath, label],
498 stdout = "/dev/tty5",
499 stderr = "/dev/tty5", searchPath = 1)
500 if rc:
501 raise SystemError
502 entry.setLabel(label)
503
504 def formatDevice(self, entry, progress, chroot='/'):
505 devicePath = entry.device.setupDevice(chroot)
506 devArgs = self.getDeviceArgs(entry.device)
507 args = [ "mke2fs", devicePath ]
508
509 fsProfileArgs = self.getFsProfileArgs()
510 if fsProfileArgs:
511 args.extend(fsProfileArgs)
512 args.extend(devArgs)
513 args.extend(self.extraFormatArgs)
514
515 log.info("Format command: %s\n" % str(args))
516
517 rc = ext2FormatFilesystem(args, "/dev/tty5",
518 progress,
519 entry.mountpoint)
520 if rc:
521 raise SystemError
522
523 def clobberDevice(self, entry, chroot):
524 device = entry.device.setupDevice(chroot)
525 isys.ext2Clobber(device)
526
527 # this is only for ext3 filesystems, but migration is a method
528 # of the ext2 fstype, so it needs to be here. FIXME should be moved
529 def setExt3Options(self, entry, message, chroot='/'):
530 devicePath = entry.device.setupDevice(chroot)
531
532 # if no journal, don't turn off the fsck
533 if not isys.ext2HasJournal(devicePath):
534 return
535
536 rc = inutil.execWithRedirect("tune2fs",
537 ["-c0", "-i0",
538 "-ouser_xattr,acl", devicePath],
539 stdout = "/dev/tty5",
540 stderr = "/dev/tty5", searchPath = 1)
541
542 class ext2FileSystem(extFileSystem):
543 def __init__(self):
544 extFileSystem.__init__(self)
545 self.name = "ext2"
546 self.partedFileSystemType = parted.file_system_type_get("ext2")
547 self.migratetofs = ['ext3']
548
549 def migrateFileSystem(self, entry, message, chroot='/'):
550 devicePath = entry.device.setupDevice(chroot)
551
552 if not entry.fsystem or not entry.origfsystem:
553 raise RuntimeError, ("Trying to migrate fs w/o fsystem or "
554 "origfsystem set")
555 if entry.fsystem.getName() != "ext3":
556 raise RuntimeError, ("Trying to migrate ext2 to something other "
557 "than ext3")
558
559 # if journal already exists skip
560 if isys.ext2HasJournal(devicePath):
561 log.info("Skipping migration of %s, has a journal already.\n" % devicePath)
562 return
563
564 rc = inutil.execWithRedirect("tune2fs",
565 ["-j", devicePath ],
566 stdout = "/dev/tty5",
567 stderr = "/dev/tty5", searchPath = 1)
568
569 if rc:
570 raise SystemError
571
572 # XXX this should never happen, but appears to have done
573 # so several times based on reports in bugzilla.
574 # At least we can avoid leaving them with a system which won't boot
575 if not isys.ext2HasJournal(devicePath):
576 log.warning("Migration of %s attempted but no journal exists after "
577 "running tune2fs.\n" % (devicePath))
578 if message:
579 rc = message(_("Error"),
580 _("An error occurred migrating %s to ext3. It is "
581 "possible to continue without migrating this "
582 "file system if desired.\n\n"
583 "Would you like to continue without migrating %s?")
584 % (devicePath, devicePath), type = "yesno")
585 if rc == 0:
586 sys.exit(0)
587 entry.fsystem = entry.origfsystem
588 else:
589 extFileSystem.setExt3Options(self, entry, message, chroot)
590
591
592 fileSystemTypeRegister(ext2FileSystem())
593
594 class ext3FileSystem(extFileSystem):
595 def __init__(self):
596 extFileSystem.__init__(self)
597 self.name = "ext3"
598 self.extraFormatArgs = [ "-t", "ext3" ]
599 self.partedFileSystemType = parted.file_system_type_get("ext3")
600 self.migratetofs = ['ext4dev']
601
602 def formatDevice(self, entry, progress, chroot='/'):
603 extFileSystem.formatDevice(self, entry, progress, chroot)
604 extFileSystem.setExt3Options(self, entry, progress, chroot)
605
606 def migrateFileSystem(self, entry, message, chroot='/'):
607 devicePath = entry.device.setupDevice(chroot)
608
609 if not entry.fsystem or not entry.origfsystem:
610 raise RuntimeError, ("Trying to migrate fs w/o fsystem or "
611 "origfsystem set")
612 if entry.fsystem.getName() != "ext4dev":
613 raise RuntimeError, ("Trying to migrate ext3 to something other "
614 "than ext4")
615
616 # This is only needed as long as ext4 is actually "ext4dev"
617 rc = inutil.execWithRedirect("tune2fs",
618 ["-E", "test_fs", devicePath ],
619 stdout = "/dev/tty5",
620 stderr = "/dev/tty5", searchPath = 1)
621 if rc:
622 raise SystemError
623
624 fileSystemTypeRegister(ext3FileSystem())
625
626 class ext4FileSystem(extFileSystem):
627 def __init__(self):
628 extFileSystem.__init__(self)
629 self.name = "ext4"
630 self.partedFileSystemType = parted.file_system_type_get("ext3")
631 self.extraFormatArgs = [ "-t", "ext4" ]
632 self.bootable = False
633
634 def formatDevice(self, entry, progress, chroot='/'):
635 extFileSystem.formatDevice(self, entry, progress, chroot)
636 extFileSystem.setExt3Options(self, entry, progress, chroot)
637
638 fileSystemTypeRegister(ext4FileSystem())
639
640 class swapFileSystem(FileSystemType):
641 enabledSwaps = {}
642
643 def __init__(self):
644 FileSystemType.__init__(self)
645 self.partedFileSystemType = parted.file_system_type_get("linux-swap")
646 self.formattable = 1
647 self.name = "swap"
648 self.maxSizeMB = 8 * 1024 * 1024
649 self.linuxnativefs = 1
650 self.supported = 1
651 self.maxLabelChars = 15
652
653 def mount(self, device, mountpoint, readOnly=0, bindMount=0, instroot = None):
654 pagesize = resource.getpagesize()
655 buf = None
656 if pagesize > 2048:
657 num = pagesize
658 else:
659 num = 2048
660 try:
661 fd = os.open(device, os.O_RDONLY)
662 buf = os.read(fd, num)
663 except:
664 pass
665 finally:
666 try:
667 os.close(fd)
668 except:
669 pass
670
671 if buf is not None and len(buf) == pagesize:
672 sig = buf[pagesize - 10:]
673 if sig == 'SWAP-SPACE':
674 raise OldSwapError
675 if sig == 'S1SUSPEND\x00' or sig == 'S2SUSPEND\x00':
676 raise SuspendError
677
678 isys.swapon(device)
679
680 def umount(self, device, path):
681 # unfortunately, turning off swap is bad.
682 raise RuntimeError, "unable to turn off swap"
683
684 def formatDevice(self, entry, progress, chroot='/'):
685 file = entry.device.setupDevice(chroot)
686 rc = inutil.execWithRedirect("mkswap",
687 ["-v1", file],
688 stdout = "/dev/tty5",
689 stderr = "/dev/tty5",
690 searchPath = 1)
691 if rc:
692 raise SystemError
693
694 def labelDevice(self, entry, chroot):
695 file = entry.device.setupDevice(chroot)
696 devName = entry.device.getDevice()
697 # we'll keep the SWAP-* naming for all devs but Compaq SMART2
698 # nodes (#176074)
699 if devName[0:6] == "cciss/":
700 swapLabel = "SW-%s" % (devName)
701 elif devName.startswith("mapper/"):
702 swapLabel = "SWAP-%s" % (devName[7:],)
703 else:
704 swapLabel = "SWAP-%s" % (devName)
705 label = labelFactory.createLabel(swapLabel, self.maxLabelChars)
706 rc = inutil.execWithRedirect("mkswap",
707 ["-v1", "-L", label, file],
708 stdout = "/dev/tty5",
709 stderr = "/dev/tty5",
710 searchPath = 1)
711 if rc:
712 raise SystemError
713 entry.setLabel(label)
714
715 def clobberDevice(self, entry, chroot):
716 pagesize = resource.getpagesize()
717 dev = entry.device.setupDevice(chroot)
718 try:
719 fd = os.open(dev, os.O_RDWR)
720 buf = "\0x00" * pagesize
721 os.write(fd, buf)
722 except:
723 pass
724 finally:
725 try:
726 os.close(fd)
727 except:
728 pass
729
730 fileSystemTypeRegister(swapFileSystem())
731
732 class FATFileSystem(FileSystemType):
733 def __init__(self):
734 FileSystemType.__init__(self)
735 self.partedFileSystemType = parted.file_system_type_get("fat32")
736 self.formattable = 0
737 self.checked = 0
738 self.maxSizeMB = 1024 * 1024
739 self.name = "vfat"
740
741 fileSystemTypeRegister(FATFileSystem())
742
743 class NTFSFileSystem(FileSystemType):
744 def __init__(self):
745 FileSystemType.__init__(self)
746 self.partedFileSystemType = parted.file_system_type_get("ntfs")
747 self.formattable = 0
748 self.checked = 0
749 self.name = "ntfs"
750
751 fileSystemTypeRegister(NTFSFileSystem())
752
753 class ForeignFileSystem(FileSystemType):
754 def __init__(self):
755 FileSystemType.__init__(self)
756 self.formattable = 0
757 self.checked = 0
758 self.name = "foreign"
759
760 def formatDevice(self, entry, progress, chroot='/'):
761 return
762
763 fileSystemTypeRegister(ForeignFileSystem())
764
765 class PsudoFileSystem(FileSystemType):
766 def __init__(self, name):
767 FileSystemType.__init__(self)
768 self.formattable = 0
769 self.checked = 0
770 self.name = name
771 self.supported = 0
772
773 def isKernelFS(self):
774 return True
775
776 class ProcFileSystem(PsudoFileSystem):
777 def __init__(self):
778 PsudoFileSystem.__init__(self, "proc")
779
780 fileSystemTypeRegister(ProcFileSystem())
781
782 class SysfsFileSystem(PsudoFileSystem):
783 def __init__(self):
784 PsudoFileSystem.__init__(self, "sysfs")
785
786 fileSystemTypeRegister(SysfsFileSystem())
787
788 class DevptsFileSystem(PsudoFileSystem):
789 def __init__(self):
790 PsudoFileSystem.__init__(self, "devpts")
791 self.defaultOptions = "gid=5,mode=620"
792
793 def isMountable(self):
794 return 0
795
796 fileSystemTypeRegister(DevptsFileSystem())
797
798 class DevshmFileSystem(PsudoFileSystem):
799 def __init__(self):
800 PsudoFileSystem.__init__(self, "tmpfs")
801
802 def isMountable(self):
803 return 0
804
805 fileSystemTypeRegister(DevshmFileSystem())
806
807 class AutoFileSystem(PsudoFileSystem):
808 def __init__(self):
809 PsudoFileSystem.__init__(self, "auto")
810
811 def mount(self, device, mountpoint, readOnly=0, bindMount=0, instroot = None):
812 errNum = 0
813 errMsg = "cannot mount auto filesystem on %s of this type" % device
814
815 if not self.isMountable():
816 return
817 inutil.mkdirChain("%s/%s" %(instroot, mountpoint))
818 for fs in getFStoTry (device):
819 try:
820 isys.mount (device, mountpoint, fstype = fs, readOnly = readOnly,
821 bindMount = bindMount)
822 return
823 except SystemError, (num, msg):
824 errNum = num
825 errMsg = msg
826 continue
827
828 raise SystemError (errNum, errMsg)
829
830 def umount(self, device, path):
831 isys.umount(path, removeDir = 0)
832
833 fileSystemTypeRegister(AutoFileSystem())
834
835 class BindFileSystem(PsudoFileSystem):
836 def __init__(self):
837 PsudoFileSystem.__init__(self, "bind")
838
839 def isMountable(self):
840 return 1
841
842 fileSystemTypeRegister(BindFileSystem())
843
844 class FileSystemSet:
845 def __init__(self):
846 self.messageWindow = None
847 self.progressWindow = None
848 self.waitWindow = None
849 self.mountcount = 0
850 self.migratedfs = 0
851 self.reset()
852 self.volumesCreated = 0
853
854 def isActive(self):
855 return self.mountcount != 0
856
857 def registerMessageWindow(self, method):
858 self.messageWindow = method
859
860 def registerProgressWindow(self, method):
861 self.progressWindow = method
862
863 def registerWaitWindow(self, method):
864 self.waitWindow = method
865
866 def reset (self):
867 self.entries = []
868 proc = FileSystemSetEntry(Device(device="proc"), '/proc',
869 fileSystemTypeGet("proc"))
870 self.add(proc)
871 sys = FileSystemSetEntry(Device(device="sys"), '/sys',
872 fileSystemTypeGet("sysfs"))
873 self.add(sys)
874 pts = FileSystemSetEntry(Device(device="devpts"), '/dev/pts',
875 fileSystemTypeGet("devpts"), "gid=5,mode=620")
876 self.add(pts)
877 shm = FileSystemSetEntry(Device(device="shm"), '/dev/shm',
878 fileSystemTypeGet("tmpfs"))
879 self.add(shm)
880
881 def verify (self):
882 for entry in self.entries:
883 if type(entry.__dict__) != type({}):
884 raise RuntimeError, "fsset internals inconsistent"
885
886 def add (self, newEntry):
887 # Should object A be sorted after object B? Take mountpoints and
888 # device names into account so bind mounts are sorted correctly.
889 def comesAfter(a, b):
890 mntA = a.mountpoint
891 mntB = b.mountpoint
892 devA = a.device.getDevice()
893 devB = b.device.getDevice()
894
895 if not mntB or not devB:
896 return True
897 if not mntA or not devA:
898 return False
899
900 if (mntA.startswith(mntB) and mntA != mntB) or (devA.startswith(mntB) and devA != devB):
901 return True
902 else:
903 return False
904
905 # Remove preexisting duplicate entries - pseudo filesystems are
906 # duplicate if they have the same filesystem type as an existing one.
907 # Otherwise, they have to have the same device and mount point
908 # (required to check for bind mounts).
909 for existing in self.entries:
910 if (isinstance (newEntry.fsystem, PsudoFileSystem) and existing.fsystem.getName() == newEntry.fsystem.getName()) or (existing.device.getDevice() == newEntry.device.getDevice() and existing.mountpoint == newEntry.mountpoint):
911 self.remove(existing)
912
913 ### debuggin'
914 #log.info ("fsset at %s\n"
915 # "adding entry for %s\n"
916 # "entry object %s, class __dict__ is %s",
917 # self, entry.mountpoint, entry,
918 # isys.printObject(entry.__dict__))
919
920 insertAt = 0
921
922 # Special case for /.
923 if newEntry.mountpoint == "/":
924 self.entries.insert(insertAt, newEntry)
925 return
926
927 # doesn't matter where these get added, so just put them at the end
928 if not newEntry.mountpoint or not newEntry.mountpoint.startswith("/") or self.entries == []:
929 self.entries.append(newEntry)
930 return
931
932 for entry in self.entries:
933 if comesAfter(newEntry, entry):
934 insertAt = self.entries.index(entry)+1
935
936 self.entries.insert(insertAt, newEntry)
937
938 def remove (self, entry):
939 self.entries.remove(entry)
940
941 def getEntryByMountPoint(self, mount):
942 for entry in self.entries:
943 if entry.mountpoint == mount:
944 return entry
945 return None
946
947 def getEntryByDeviceName(self, dev):
948 for entry in self.entries:
949 if entry.device.getDevice() == dev:
950 return entry
951 return None
952
953 def copy(self):
954 new = FileSystemSet()
955 for entry in self.entries:
956 new.add(entry)
957 return new
958
959 def fstab (self):
960 format = "%-23s %-23s %-7s %-15s %d %d\n"
961 fstab = """
962 #
963 # /etc/fstab
964 # Created by pomona on %s
965 #
966 # Accessible filesystems, by reference, are maintained under '/dev/disk'
967 # See man pages fstab(5), findfs(8), mount(8) and/or vol_id(8) for more info
968 #
969 """ % time.asctime()
970
971 for entry in self.entries:
972 if entry.mountpoint:
973 if entry.getUuid() and entry.device.doLabel is not None:
974 device = "UUID=%s" %(entry.getUuid(),)
975 elif entry.getLabel() and entry.device.doLabel is not None:
976 device = "LABEL=%s" % (entry.getLabel(),)
977 else:
978 device = devify(entry.device.getDevice())
979 fstab = fstab + entry.device.getComment()
980 fstab = fstab + format % (device, entry.mountpoint,
981 entry.fsystem.getMountName(),
982 entry.getOptions(), entry.fsck,
983 entry.order)
984 return fstab
985
986 def mtab(self):
987 format = "%s %s %s %s 0 0\n"
988 mtab = ""
989 for entry in self.entries:
990 if not entry.isMounted():
991 continue
992 if entry.mountpoint:
993 # swap doesn't end up in the mtab
994 if entry.fsystem.getName() == "swap":
995 continue
996 if entry.options:
997 options = "rw," + entry.options
998 else:
999 options = "rw"
1000 mtab = mtab + format % (devify(entry.device.getDevice()),
1001 entry.mountpoint,
1002 entry.fsystem.getName(),
1003 options)
1004 return mtab
1005
1006
1007 def write(self, prefix):
1008 f = open(prefix + "/etc/fstab", "w")
1009 f.write(self.fstab())
1010 f.close()
1011
1012 # touch mtab
1013 open(prefix + "/etc/mtab", "w+")
1014 f.close()
1015
1016 ## XXX
1017 def mkDevRoot(self, instPath):
1018 root = self.getEntryByMountPoint("/")
1019 dev = "%s/dev/%s" % (instPath, root.device.getDevice())
1020 rdev = os.stat(dev).st_rdev
1021
1022 #if not os.path.exists("%s/dev/root" %(instPath,)):
1023 # os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev)
1024
1025 # return the "boot" device
1026 def getBootDev(self):
1027 mntDict = {}
1028 bootDev = None
1029 for entry in self.entries:
1030 mntDict[entry.mountpoint] = entry.device
1031
1032 if mntDict.has_key("/boot"):
1033 bootDev = mntDict['/boot']
1034 elif mntDict.has_key("/"):
1035 bootDev = mntDict['/']
1036
1037 return bootDev
1038
1039 def bootloaderChoices(self, diskSet, bl):
1040 ret = {}
1041 bootDev = self.getBootDev()
1042
1043 if bootDev is None:
1044 log.warning("no boot device set")
1045 return ret
1046
1047 ret['boot'] = (bootDev.device, N_("First sector of boot partition"))
1048 ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
1049 return ret
1050
1051 # set active partition on disks
1052 # if an active partition is set, leave it alone; if none set
1053 # set either our boot partition or the first partition on the drive active
1054 def setActive(self, diskset):
1055 dev = self.getBootDev()
1056
1057 if dev is None:
1058 return
1059
1060 bootDev = dev.device
1061
1062 for drive in diskset.disks.keys():
1063 foundActive = 0
1064 bootPart = None
1065 disk = diskset.disks[drive]
1066 part = disk.next_partition()
1067 while part:
1068 if not part.is_active():
1069 part = disk.next_partition(part)
1070 continue
1071
1072 if not part.is_flag_available(parted.PARTITION_BOOT):
1073 foundActive = 1
1074 part = None
1075 continue
1076
1077 if part.get_flag(parted.PARTITION_BOOT):
1078 foundActive = 1
1079 part = None
1080 continue
1081
1082 if not bootPart:
1083 bootPart = part
1084
1085 if partedUtils.get_partition_name(part) == bootDev:
1086 bootPart = part
1087
1088 part = disk.next_partition(part)
1089
1090 if bootPart and not foundActive:
1091 bootPart.set_flag(parted.PARTITION_BOOT, 1)
1092
1093 if bootPart:
1094 del bootPart
1095
1096 def formatSwap(self, chroot, forceFormat=False):
1097 formatted = []
1098 notformatted = []
1099
1100 for entry in self.entries:
1101 if (not entry.fsystem or not entry.fsystem.getName() == "swap" or
1102 entry.isMounted()):
1103 continue
1104 if not entry.getFormat():
1105 if not forceFormat:
1106 notformatted.append(entry)
1107 continue
1108 try:
1109 self.formatEntry(entry, chroot)
1110 formatted.append(entry)
1111 except SystemError:
1112 if self.messageWindow:
1113 self.messageWindow(_("Error"),
1114 _("An error occurred trying to "
1115 "initialize swap on device %s. This "
1116 "problem is serious, and the install "
1117 "cannot continue.\n\n"
1118 "Press <Enter> to reboot your system.")
1119 % (entry.device.getDevice(),))
1120 sys.exit(0)
1121
1122 for entry in formatted:
1123 try:
1124 self.labelEntry(entry, chroot)
1125 except SystemError:
1126 # should be OK, fall back to by device
1127 pass
1128
1129 # find if there's a label on the ones we're not formatting
1130 for entry in notformatted:
1131 dev = entry.device.getDevice()
1132 if not dev or dev == "none":
1133 continue
1134 try:
1135 label = isys.readFSLabel(dev)
1136 except:
1137 continue
1138 if label:
1139 entry.setLabel(label)
1140
1141 def turnOnSwap(self, chroot, upgrading=False):
1142 def swapErrorDialog (msg, format_button_text, entry):
1143 buttons = [_("Skip"), format_button_text, _("Reboot")]
1144 ret = self.messageWindow(_("Error"), msg, type="custom",
1145 custom_buttons=buttons,
1146 custom_icon="warning")
1147 if ret == 0:
1148 self.entries.remove(entry)
1149 elif ret == 1:
1150 self.formatEntry(entry, chroot)
1151 entry.mount(chroot)
1152 self.mountcount = self.mountcount + 1
1153 else:
1154 sys.exit(0)
1155
1156 for entry in self.entries:
1157 if (entry.fsystem and entry.fsystem.getName() == "swap"
1158 and not entry.isMounted()):
1159 try:
1160 entry.mount(chroot)
1161 self.mountcount = self.mountcount + 1
1162 except OldSwapError:
1163 if self.messageWindow:
1164 msg = _("The swap device:\n\n /dev/%s\n\n"
1165 "is a version 0 Linux swap partition. If you "
1166 "want to use this device, you must reformat as "
1167 "a version 1 Linux swap partition. If you skip "
1168 "it, the installer will ignore it during the "
1169 "installation.") % (entry.device.getDevice())
1170
1171 swapErrorDialog(msg, _("Reformat"), entry)
1172 except SuspendError:
1173 if self.messageWindow:
1174 if upgrading:
1175 msg = _("The swap device:\n\n /dev/%s\n\n"
1176 "in your /etc/fstab file is currently in "
1177 "use as a software suspend partition, "
1178 "which means your system is hibernating. "
1179 "To perform an upgrade, please shut down "
1180 "your system rather than hibernating it.") \
1181 % (entry.device.getDevice())
1182 else:
1183 msg = _("The swap device:\n\n /dev/%s\n\n"
1184 "in your /etc/fstab file is currently in "
1185 "use as a software suspend partition, "
1186 "which means your system is hibernating. "
1187 "If you are performing a new install, "
1188 "make sure the installer is set "
1189 "to format all swap partitions.") \
1190 % (entry.device.getDevice())
1191
1192 # choose your own adventure swap partitions...
1193 msg = msg + _("\n\nChoose Skip if you want the "
1194 "installer to ignore this partition during "
1195 "the upgrade. Choose Format to reformat "
1196 "the partition as swap space. Choose Reboot "
1197 "to restart the system.")
1198
1199 swapErrorDialog(msg, _("Format"), entry)
1200 else:
1201 sys.exit(0)
1202
1203 except SystemError, (num, msg):
1204 if self.messageWindow:
1205 if upgrading:
1206 self.messageWindow(_("Error"),
1207 _("Error enabling swap device "
1208 "%s: %s\n\n"
1209 "The /etc/fstab on your "
1210 "upgrade partition does not "
1211 "reference a valid swap "
1212 "partition.\n\n"
1213 "Press OK to reboot your "
1214 "system.")
1215 % (entry.device.getDevice(), msg))
1216 else:
1217 self.messageWindow(_("Error"),
1218 _("Error enabling swap device "
1219 "%s: %s\n\n"
1220 "This most likely means this "
1221 "swap partition has not been "
1222 "initialized.\n\n"
1223 "Press OK to reboot your "
1224 "system.")
1225 % (entry.device.getDevice(), msg))
1226 sys.exit(0)
1227
1228 def labelEntry(self, entry, chroot):
1229 label = entry.device.getLabel()
1230 if label:
1231 entry.setLabel(label)
1232 if labelFactory.isLabelReserved(label):
1233 entry.device.doLabel = 1
1234 if entry.device.doLabel is not None:
1235 entry.fsystem.labelDevice(entry, chroot)
1236
1237 def formatEntry(self, entry, chroot):
1238 if entry.mountpoint:
1239 log.info("formatting %s as %s" %(entry.mountpoint, entry.fsystem.name))
1240 entry.fsystem.clobberDevice(entry, chroot)
1241 entry.fsystem.formatDevice(entry, self.progressWindow, chroot)
1242
1243 def badblocksEntry(self, entry, chroot):
1244 entry.fsystem.badblocksDevice(entry, self.progressWindow, chroot)
1245
1246 def getMigratableEntries(self):
1247 retval = []
1248 for entry in self.entries:
1249 if entry.origfsystem and entry.origfsystem.isMigratable():
1250 retval.append(entry)
1251
1252 return retval
1253
1254 def formattablePartitions(self):
1255 list = []
1256 for entry in self.entries:
1257 if entry.fsystem.isFormattable():
1258 list.append (entry)
1259 return list
1260
1261 def checkBadblocks(self, chroot='/'):
1262 for entry in self.entries:
1263 if (not entry.fsystem.isFormattable() or not entry.getBadblocks()
1264 or entry.isMounted()):
1265 continue
1266 try:
1267 self.badblocksEntry(entry, chroot)
1268 except BadBlocksError:
1269 log.error("Bad blocks detected on device %s",entry.device.getDevice())
1270 if self.messageWindow:
1271 self.messageWindow(_("Error"),
1272 _("Bad blocks have been detected on "
1273 "device /dev/%s. We do "
1274 "not recommend you use this device."
1275 "\n\n"
1276 "Press <Enter> to reboot your system")
1277 % (entry.device.getDevice(),))
1278 sys.exit(0)
1279
1280 except SystemError:
1281 if self.messageWindow:
1282 self.messageWindow(_("Error"),
1283 _("An error occurred searching for "
1284 "bad blocks on %s. This problem is "
1285 "serious, and the install cannot "
1286 "continue.\n\n"
1287 "Press <Enter> to reboot your system.")
1288 % (entry.device.getDevice(),))
1289 sys.exit(0)
1290
1291 def makeFilesystems(self, chroot='/'):
1292 formatted = []
1293 notformatted = []
1294 for entry in self.entries:
1295 if (not entry.fsystem.isFormattable() or not entry.getFormat()
1296 or entry.isMounted()):
1297 notformatted.append(entry)
1298 continue
1299 try:
1300 self.formatEntry(entry, chroot)
1301 formatted.append(entry)
1302 except SystemError:
1303 if self.messageWindow:
1304 self.messageWindow(_("Error"),
1305 _("An error occurred trying to "
1306 "format %s. This problem is "
1307 "serious, and the install cannot "
1308 "continue.\n\n"
1309 "Press <Enter> to reboot your system.")
1310 % (entry.device.getDevice(),))
1311 sys.exit(0)
1312
1313 for entry in formatted:
1314 try:
1315 self.labelEntry(entry, chroot)
1316 except SystemError:
1317 # should be OK, we'll still use the device name to mount.
1318 pass
1319
1320 # go through and have labels for the ones we don't format
1321 for entry in notformatted:
1322 dev = entry.device.getDevice()
1323 if not dev or dev == "none":
1324 continue
1325 if not entry.mountpoint or entry.mountpoint == "swap":
1326 continue
1327 try:
1328 label = isys.readFSLabel(dev)
1329 except:
1330 continue
1331 if label:
1332 entry.setLabel(label)
1333 else:
1334 self.labelEntry(entry, chroot)
1335
1336 def haveMigratedFilesystems(self):
1337 return self.migratedfs
1338
1339 def migrateFilesystems(self, chroot='/'):
1340 if self.migratedfs:
1341 return
1342
1343 for entry in self.entries:
1344 if not entry.origfsystem:
1345 continue
1346
1347 if not entry.origfsystem.isMigratable() or not entry.getMigrate():
1348 continue
1349 try:
1350 entry.origfsystem.migrateFileSystem(entry, self.messageWindow, chroot)
1351 except SystemError:
1352 if self.messageWindow:
1353 self.messageWindow(_("Error"),
1354 _("An error occurred trying to "
1355 "migrate %s. This problem is "
1356 "serious, and the install cannot "
1357 "continue.\n\n"
1358 "Press <Enter> to reboot your system.")
1359 % (entry.device.getDevice(),))
1360 sys.exit(0)
1361
1362 self.migratedfs = 1
1363
1364 def mountFilesystems(self, pomona, raiseErrors = 0, readOnly = 0):
1365 #protected = pomona.method.protectedPartitions()
1366 protected = []
1367
1368 for entry in self.entries:
1369 # Don't try to mount a protected partition, since it will already
1370 # have been mounted as the installation source.
1371 if not entry.fsystem.isMountable() or (protected and entry.device.getDevice() in protected):
1372 continue
1373
1374 try:
1375 log.info("trying to mount %s on %s" %(entry.device.getDevice(), entry.mountpoint))
1376 entry.mount(pomona.rootPath, readOnly = readOnly)
1377 self.mountcount = self.mountcount + 1
1378 except OSError, (num, msg):
1379 if self.messageWindow:
1380 if num == errno.EEXIST:
1381 self.messageWindow(_("Invalid mount point"),
1382 _("An error occurred when trying "
1383 "to create %s. Some element of "
1384 "this path is not a directory. "
1385 "This is a fatal error and the "
1386 "install cannot continue.\n\n"
1387 "Press <Enter> to reboot your "
1388 "system.") % (entry.mountpoint,))
1389 else:
1390 self.messageWindow(_("Invalid mount point"),
1391 _("An error occurred when trying "
1392 "to create %s: %s. This is "
1393 "a fatal error and the install "
1394 "cannot continue.\n\n"
1395 "Press <Enter> to reboot your "
1396 "system.") % (entry.mountpoint, msg))
1397 sys.exit(0)
1398 except SystemError, (num, msg):
1399 if raiseErrors:
1400 raise SystemError, (num, msg)
1401
1402 if self.messageWindow:
1403 if not entry.fsystem.isLinuxNativeFS():
1404 ret = self.messageWindow(_("Unable to mount filesystem"),
1405 _("An error occurred mounting "
1406 "device %s as %s. You may "
1407 "continue installation, but "
1408 "there may be problems.")
1409 % (entry.device.getDevice(),
1410 entry.mountpoint),
1411 type="custom", custom_icon="warning",
1412 custom_buttons=[_("_Reboot"), _("_Continue")])
1413
1414 if ret == 0:
1415 sys.exit(0)
1416 else:
1417 continue
1418 else:
1419 if pomona.id.getUpgrade() and not entry.getLabel():
1420 errStr = _("Error mounting device %s as %s: "
1421 "%s\n\n"
1422 "Devices in /etc/fstab should be "
1423 "specified by label, not by device name."
1424 "\n\n"
1425 "Press OK to reboot your system.") % (entry.device.getDevice(), entry.mountpoint, msg)
1426 else:
1427 errStr = _("Error mounting device %s as %s: "
1428 "%s\n\n"
1429 "This most likely means this "
1430 "partition has not been formatted."
1431 "\n\n"
1432 "Press OK to reboot your system.") % (entry.device.getDevice(), entry.mountpoint, msg)
1433
1434 self.messageWindow(_("Error"), errStr)
1435
1436 sys.exit(0)
1437
1438 def filesystemSpace(self, chroot='/'):
1439 space = []
1440 for entry in self.entries:
1441 if not entry.isMounted():
1442 continue
1443 # we can't put swap files on swap partitions; that's nonsense
1444 if entry.mountpoint == "swap":
1445 continue
1446 path = "%s/%s" % (chroot, entry.mountpoint)
1447 try:
1448 space.append((entry.mountpoint, isys.pathSpaceAvailable(path)))
1449 except SystemError:
1450 log.error("failed to get space available in filesystemSpace() for %s" %(entry.mountpoint,))
1451
1452 def spaceSort(a, b):
1453 (m1, s1) = a
1454 (m2, s2) = b
1455
1456 if (s1 > s2):
1457 return -1
1458 elif s1 < s2:
1459 return 1
1460 return 0
1461
1462 space.sort(spaceSort)
1463 return space
1464
1465 def hasDirtyFilesystems(self, mountpoint):
1466 ret = []
1467
1468 for entry in self.entries:
1469 # XXX - multifsify, virtualize isdirty per fstype
1470 if entry.fsystem.getName() != "ext2": continue
1471 if entry.getFormat(): continue
1472 if isinstance(entry.device.getDevice(), BindMountDevice): continue
1473
1474 try:
1475 if isys.ext2IsDirty(entry.device.getDevice()):
1476 log.info("%s is a dirty ext2 partition" % entry.device.getDevice())
1477 ret.append(entry.device.getDevice())
1478 except Exception, e:
1479 log.error("got an exception checking %s for being dirty, hoping it's not" %(entry.device.getDevice(),))
1480
1481 return ret
1482
1483 def umountFilesystems(self, instPath, ignoreErrors = 0, swapoff = True):
1484 # XXX remove special case
1485 try:
1486 isys.umount(instPath + '/proc/bus/usb', removeDir = 0)
1487 log.info("Umount USB OK")
1488 except:
1489 # log.error("Umount USB Fail")
1490 pass
1491
1492 # take a slice so we don't modify self.entries
1493 reverse = self.entries[:]
1494 reverse.reverse()
1495
1496 for entry in reverse:
1497 if entry.mountpoint == "swap" and not swapoff:
1498 continue
1499 entry.umount(instPath)
1500
1501 class FileSystemSetEntry:
1502 def __init__ (self, device, mountpoint,
1503 fsystem=None, options=None,
1504 origfsystem=None, migrate=0,
1505 order=-1, fsck=-1, format=0,
1506 badblocks = 0, bytesPerInode=4096, fsprofile=None):
1507 if not fsystem:
1508 fsystem = fileSystemTypeGet("ext2")
1509 self.device = device
1510 self.mountpoint = mountpoint
1511 self.fsystem = fsystem
1512 self.origfsystem = origfsystem
1513 self.migrate = migrate
1514 if options:
1515 self.options = options
1516 else:
1517 self.options = fsystem.getDefaultOptions(mountpoint)
1518 self.options += device.getDeviceOptions()
1519 self.mountcount = 0
1520 self.label = None
1521 if fsck == -1:
1522 self.fsck = fsystem.isChecked()
1523 else:
1524 self.fsck = fsck
1525 if order == -1:
1526 if mountpoint == '/':
1527 self.order = 1
1528 elif self.fsck:
1529 self.order = 2
1530 else:
1531 self.order = 0
1532 else:
1533 self.order = order
1534 if format and not fsystem.isFormattable():
1535 raise RuntimeError, ("file system type %s is not formattable, "
1536 "but has been added to fsset with format "
1537 "flag on" % fsystem.getName())
1538 self.format = format
1539 self.badblocks = badblocks
1540 self.bytesPerInode = bytesPerInode
1541 self.fsprofile = fsprofile
1542
1543 def mount(self, chroot='/', devPrefix='/dev', readOnly = 0):
1544 device = self.device.setupDevice(chroot, devPrefix=devPrefix)
1545
1546 # FIXME: we really should migrate before turnOnFilesystems.
1547 # but it's too late now
1548 if (self.migrate == 1) and (self.origfsystem is not None):
1549 self.origfsystem.mount(device, "%s" % (self.mountpoint,),
1550 readOnly = readOnly,
1551 bindMount = isinstance(self.device,
1552 BindMountDevice),
1553 instroot = chroot)
1554 else:
1555 self.fsystem.mount(device, "%s" % (self.mountpoint,),
1556 readOnly = readOnly,
1557 bindMount = isinstance(self.device,
1558 BindMountDevice),
1559 instroot = chroot)
1560
1561 self.mountcount = self.mountcount + 1
1562
1563 def umount(self, chroot='/'):
1564 if self.mountcount > 0:
1565 try:
1566 self.fsystem.umount(self.device, "%s/%s" % (chroot, self.mountpoint))
1567 self.mountcount = self.mountcount - 1
1568 except RuntimeError:
1569 pass
1570
1571 def setFileSystemType(self, fstype):
1572 self.fsystem = fstype
1573
1574 def setBadblocks(self, state):
1575 self.badblocks = state
1576
1577 def getBadblocks(self):
1578 return self.badblocks
1579
1580 def getMountPoint(self):
1581 return self.mountpoint
1582
1583 def getOptions(self):
1584 options = self.options
1585 if not options:
1586 options = self.fsystem.getDefaultOptions(self.mountpoint)
1587 return options + self.device.getDeviceOptions()
1588
1589 def setFormat (self, state):
1590 if self.migrate and state:
1591 raise ValueError, "Trying to set format bit on when migrate is set!"
1592 self.format = state
1593
1594 def getFormat (self):
1595 return self.format
1596
1597 def setMigrate (self, state):
1598 if self.format and state:
1599 raise ValueError, "Trying to set migrate bit on when format is set!"
1600 self.migrate = state
1601
1602 def getMigrate (self):
1603 return self.migrate
1604
1605 def isMounted (self):
1606 return self.mountcount > 0
1607
1608 def getLabel (self):
1609 return self.label
1610
1611 def getUuid (self):
1612 return isys.readFSUuid(self.device.getDevice())
1613
1614 def setLabel (self, label):
1615 self.label = label
1616
1617 def __str__(self):
1618 if not self.mountpoint:
1619 mntpt = "None"
1620 else:
1621 mntpt = self.mountpoint
1622
1623 str = ("fsentry -- device: %(device)s mountpoint: %(mountpoint)s\n"
1624 " fsystem: %(fsystem)s format: %(format)s\n"
1625 " ismounted: %(mounted)s options: '%(options)s'\n"
1626 " label: %(label)s fsprofile: %(fsprofile)s\n"%
1627 {"device": self.device.getDevice(), "mountpoint": mntpt,
1628 "fsystem": self.fsystem.getName(), "format": self.format,
1629 "mounted": self.mountcount, "options": self.getOptions(),
1630 "label": self.label, "fsprofile": self.fsprofile})
1631 return str
1632
1633
1634 class Device:
1635 def __init__(self, device = "none"):
1636 self.device = device
1637 self.label = None
1638 self.isSetup = 0
1639 self.doLabel = 1
1640 self.deviceOptions = ""
1641
1642 def getComment (self):
1643 return ""
1644
1645 def getDevice (self, asBoot = 0):
1646 return self.device
1647
1648 def setupDevice (self, chroot='/', devPrefix='/dev'):
1649 return self.device
1650
1651 def cleanupDevice (self, chroot, devPrefix='/dev'):
1652 pass
1653
1654 def solidify (self):
1655 pass
1656
1657 def getName(self):
1658 return self.__class__.__name__
1659
1660 def getLabel(self):
1661 try:
1662 return isys.readFSLabel(self.setupDevice())
1663 except:
1664 return ""
1665
1666 def setAsNetdev(self):
1667 """Ensure we're set up so that _netdev is in our device options."""
1668 if "_netdev" not in self.deviceOptions:
1669 self.deviceOptions += ",_netdev"
1670
1671 def isNetdev(self):
1672 """Check to see if we're set as a netdev"""
1673 if "_netdev" in self.deviceOptions:
1674 return True
1675 return False
1676
1677 def getDeviceOptions(self):
1678 return self.deviceOptions
1679
1680 class DevDevice(Device):
1681 """ Device with a device node rooted in /dev that we just always use
1682 the pre-created device node for."""
1683 def __init__(self, dev):
1684 Device.__init__(self)
1685 self.device = dev
1686
1687 def getDevice(self, asBoot = 0):
1688 return self.device
1689
1690 def setupDevice(self, chroot='/', devPrefix='/dev'):
1691 return "/dev/%s" %(self.getDevice(),)
1692
1693
1694 ext2 = fileSystemTypeGet("ext2")
1695
1696 class PartitionDevice(Device):
1697 def __init__(self, partition):
1698 Device.__init__(self)
1699 if type(partition) != types.StringType:
1700 raise ValueError, "partition must be a string"
1701 self.device = partition
1702
1703 def setupDevice(self, chroot="/", devPrefix='/dev'):
1704 path = '%s/%s' % (devPrefix, self.getDevice(),)
1705 return path
1706
1707 class PartedPartitionDevice(PartitionDevice):
1708 def __init__(self, partition):
1709 Device.__init__(self)
1710 self.device = None
1711 self.partition = partition
1712
1713 def getDevice(self, asBoot = 0):
1714 if not self.partition:
1715 return self.device
1716
1717 return partedUtils.get_partition_name(self.partition)
1718
1719 def solidify(self):
1720 # drop reference on the parted partition object and note
1721 # the current minor number allocation
1722 self.device = self.getDevice()
1723 self.partition = None
1724
1725 class BindMountDevice(Device):
1726 def __init__(self, directory):
1727 Device.__init__(self)
1728 self.device = directory
1729
1730 def setupDevice(self, chroot="/", devPrefix="/tmp"):
1731 return chroot + self.device
1732
1733 class SwapFileDevice(Device):
1734 def __init__(self, file):
1735 Device.__init__(self)
1736 self.device = file
1737 self.size = 0
1738
1739 def setSize (self, size):
1740 self.size = size
1741
1742 def setupDevice (self, chroot="/", devPrefix='/tmp'):
1743 file = os.path.normpath(chroot + self.getDevice())
1744 if not os.access(file, os.R_OK):
1745 if self.size:
1746 # make sure the permissions are set properly
1747 fd = os.open(file, os.O_CREAT, 0600)
1748 os.close(fd)
1749 isys.ddfile(file, self.size, None)
1750 else:
1751 raise SystemError, (0, "swap file creation necessary, but "
1752 "required size is unknown.")
1753 return file
1754
1755 # This is a device that describes a swap file that is sitting on
1756 # the loopback filesystem host for partitionless installs.
1757 # The piggypath is the place where the loopback file host filesystem
1758 # will be mounted
1759 class PiggybackSwapFileDevice(SwapFileDevice):
1760 def __init__(self, piggypath, file):
1761 SwapFileDevice.__init__(self, file)
1762 self.piggypath = piggypath
1763
1764 def setupDevice(self, chroot="/", devPrefix='/tmp'):
1765 return SwapFileDevice.setupDevice(self, self.piggypath, devPrefix)
1766
1767 class LoopbackDevice(Device):
1768 def __init__(self, hostPartition, hostFs):
1769 Device.__init__(self)
1770 self.host = "/dev/" + hostPartition
1771 self.hostfs = hostFs
1772 self.device = "loop1"
1773
1774 def setupDevice(self, chroot="/", devPrefix='/tmp/'):
1775 if not self.isSetup:
1776 isys.mount(self.host[5:], "/mnt/loophost", fstype = "vfat")
1777 self.device = allocateLoopback("/mnt/loophost/redhat.img")
1778 if not self.device:
1779 raise SystemError, "Unable to allocate loopback device"
1780 self.isSetup = 1
1781 path = '%s/%s' % (devPrefix, self.getDevice())
1782 else:
1783 path = '%s/%s' % (devPrefix, self.getDevice())
1784 #isys.makeDevInode(self.getDevice(), path)
1785 path = os.path.normpath(path)
1786 return path
1787
1788 def getComment (self):
1789 return "# LOOP1: %s %s /redhat.img\n" % (self.host, self.hostfs)
1790
1791 def makeDevice(dev):
1792 device = DevDevice(dev)
1793 return device
1794
1795 def readFstab(pomona):
1796 path = pomona.rootPath + '/etc/fstab'
1797 intf = pomona.intf
1798 fsset = FileSystemSet()
1799
1800 # first, we look at all the disks on the systems and get any ext2/3
1801 # labels off of the filesystem.
1802 # temporary, to get the labels
1803 diskset = partedUtils.DiskSet(pomona)
1804 diskset.openDevices()
1805 labels = diskset.getLabels()
1806
1807 labelToDevice = {}
1808 for device, label in labels.items():
1809 if not labelToDevice.has_key(label):
1810 labelToDevice[label] = device
1811 elif intf is not None:
1812 try:
1813 intf.messageWindow(_("Duplicate Labels"),
1814 _("Multiple devices on your system are "
1815 "labelled %s. Labels across devices must be "
1816 "unique for your system to function "
1817 "properly.\n\n"
1818 "Please fix this problem and restart the "
1819 "installation process.")
1820 % (label,), type="custom", custom_icon="error",
1821 custom_buttons=[_("_Reboot")])
1822 except TypeError:
1823 intf.messageWindow(_("Invalid Label"),
1824 _("An invalid label was found on device "
1825 "%s. Please fix this problem and restart "
1826 "the installation process.")
1827 % (device,), type="custom", custom_icon="error",
1828 custom_buttons=[_("_Reboot")])
1829
1830 sys.exit(0)
1831 else:
1832 log.warning("Duplicate labels for %s, but no intf so trying "
1833 "to continue" % (label,))
1834
1835 # mark these labels found on the system as used so the factory
1836 # doesn't give them to another device
1837 labelFactory.reserveLabels(labels)
1838
1839 loopIndex = {}
1840
1841 f = open (path, "r")
1842 lines = f.readlines ()
1843 f.close()
1844
1845 for line in lines:
1846 fields = string.split(line)
1847
1848 if not fields: continue
1849
1850 if line[0] == "#":
1851 # skip all comments
1852 continue
1853
1854 # all valid fstab entries have 6 fields; if the last two are missing
1855 # they are assumed to be zero per fstab(5)
1856 if len(fields) < 4:
1857 continue
1858 elif len(fields) == 4:
1859 fields.append(0)
1860 fields.append(0)
1861 elif len(fields) == 5:
1862 fields.append(0)
1863 elif len(fields) > 6:
1864 continue
1865
1866 if string.find(fields[3], "noauto") != -1: continue
1867
1868 # shenanigans to handle ext3,ext2 format in fstab
1869 fstotry = fields[2]
1870 if fstotry.find(","):
1871 fstotry = fstotry.split(",")
1872 else:
1873 fstotry = [ fstotry ]
1874 fsystem = None
1875 for fs in fstotry:
1876 # if we don't support mounting the filesystem, continue
1877 if not fileSystemTypes.has_key(fs):
1878 continue
1879 fsystem = fileSystemTypeGet(fs)
1880 break
1881 # "none" is valid as an fs type for bind mounts (#151458)
1882 if fsystem is None and (string.find(fields[3], "bind") == -1):
1883 continue
1884 label = None
1885 if fields[0] == "none":
1886 device = Device()
1887 elif ((string.find(fields[3], "bind") != -1) and fields[0].startswith("/")):
1888 # it's a bind mount, they're Weird (tm)
1889 device = BindMountDevice(fields[0])
1890 fsystem = fileSystemTypeGet("bind")
1891 elif len(fields) >= 6 and fields[0].startswith('LABEL='):
1892 label = fields[0][6:]
1893 if labelToDevice.has_key(label):
1894 device = makeDevice(labelToDevice[label])
1895 else:
1896 log.warning ("fstab file has LABEL=%s, but this label "
1897 "could not be found on any file system", label)
1898 # bad luck, skip this entry.
1899 continue
1900 elif fields[2] == "swap" and not fields[0].startswith('/dev/'):
1901 # swap files
1902 file = fields[0]
1903 if file.startswith('/initrd/loopfs/'):
1904 file = file[14:]
1905 device = PiggybackSwapFileDevice("/mnt/loophost", file)
1906 else:
1907 device = SwapFileDevice(file)
1908 elif fields[0].startswith('/dev/loop'):
1909 # look up this loop device in the index to find the
1910 # partition that houses the filesystem image
1911 # XXX currently we assume /dev/loop1
1912 if loopIndex.has_key(device):
1913 (dev, fs) = loopIndex[device]
1914 device = LoopbackDevice(dev, fs)
1915 elif fields[0].startswith('/dev/'):
1916 device = makeDevice(fields[0][5:])
1917 else:
1918 device = Device(device = fields[0])
1919
1920 # if they have a filesystem being mounted as auto, we need
1921 # to sniff around a bit to figure out what it might be
1922 # if we fail at all, though, just ignore it
1923 if fsystem == "auto" and device.getDevice() != "none":
1924 try:
1925 tmp = partedUtils.sniffFilesystemType("/dev/%s" %(device.setupDevice(),))
1926 if tmp is not None:
1927 fsystem = tmp
1928 except:
1929 pass
1930
1931 entry = FileSystemSetEntry(device, fields[1], fsystem, fields[3],
1932 origfsystem=fsystem)
1933 if label:
1934 entry.setLabel(label)
1935 fsset.add(entry)
1936 return fsset
1937
1938 def getDevFD(device):
1939 try:
1940 fd = os.open(device, os.O_RDONLY)
1941 except:
1942 file = '/dev/' + device
1943 try:
1944 fd = os.open(file, os.O_RDONLY)
1945 except:
1946 return -1
1947 return fd
1948
1949 def isValidExt2(device):
1950 fd = getDevFD(device)
1951 if fd == -1:
1952 return 0
1953
1954 buf = os.read(fd, 2048)
1955 os.close(fd)
1956
1957 if len(buf) != 2048:
1958 return 0
1959
1960 if struct.unpack("<H", buf[1080:1082]) == (0xef53,):
1961 return 1
1962
1963 return 0
1964
1965 def isValidXFS(device):
1966 fd = getDevFD(device)
1967 if fd == -1:
1968 return 0
1969
1970 buf = os.read(fd, 4)
1971 os.close(fd)
1972
1973 if len(buf) != 4:
1974 return 0
1975
1976 if buf == "XFSB":
1977 return 1
1978
1979 return 0
1980
1981 def isValidReiserFS(device):
1982 fd = getDevFD(device)
1983 if fd == -1:
1984 return 0
1985
1986 '''
1987 ** reiserfs 3.5.x super block begins at offset 8K
1988 ** reiserfs 3.6.x super block begins at offset 64K
1989 All versions have a magic value of "ReIsEr" at
1990 offset 0x34 from start of super block
1991 '''
1992 reiserMagicVal = "ReIsEr"
1993 reiserMagicOffset = 0x34
1994 reiserSBStart = [64*1024, 8*1024]
1995 bufSize = 0x40 # just large enough to include the magic value
1996 for SBOffset in reiserSBStart:
1997 try:
1998 os.lseek(fd, SBOffset, 0)
1999 buf = os.read(fd, bufSize)
2000 except:
2001 buf = ""
2002
2003 if len(buf) < bufSize:
2004 continue
2005
2006 if (buf[reiserMagicOffset:reiserMagicOffset+len(reiserMagicVal)] == reiserMagicVal):
2007 os.close(fd)
2008 return 1
2009
2010 os.close(fd)
2011 return 0
2012
2013 # this will return a list of types of filesystems which device
2014 # looks like it could be to try mounting as
2015 def getFStoTry(device):
2016 rc = []
2017
2018 if isValidXFS(device):
2019 rc.append("xfs")
2020
2021 if isValidReiserFS(device):
2022 rc.append("reiserfs")
2023
2024 if isValidExt2(device):
2025 if isys.ext2HasJournal(device):
2026 rc.append("ext3")
2027 rc.append("ext2")
2028
2029 ### XXX FIXME: need to check for swap
2030
2031 return rc
2032
2033 def allocateLoopback(file):
2034 found = 1
2035 for i in range(8):
2036 dev = "loop%d" % (i,)
2037 path = "/dev/loop%d" % (i,)
2038 try:
2039 isys.losetup(path, file)
2040 found = 1
2041 except SystemError:
2042 continue
2043 break
2044 if found:
2045 return dev
2046 return None
2047
2048 def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint):
2049 if windowCreator:
2050 w = windowCreator(_("Formatting"),
2051 _("Formatting %s file system...") % (mntpoint,), 100)
2052 else:
2053 w = None
2054
2055 fd = os.open(messageFile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
2056 p = os.pipe()
2057 childpid = os.fork()
2058 if not childpid:
2059 os.close(p[0])
2060 os.dup2(p[1], 1)
2061 os.dup2(fd, 2)
2062 os.close(p[1])
2063 os.close(fd)
2064 os.execvp(argList[0], argList)
2065 log.critical("failed to exec %s", argList)
2066 os._exit(1)
2067
2068 os.close(p[1])
2069
2070 # ignoring SIGCHLD would be cleaner then ignoring EINTR, but
2071 # we can't use signal() in this thread?
2072
2073 s = 'a'
2074 while s and s != '\b':
2075 try:
2076 s = os.read(p[0], 1)
2077 except OSError, args:
2078 (num, str) = args
2079 if (num != 4):
2080 raise IOError, args
2081
2082 os.write(fd, s)
2083
2084 num = ''
2085 while s:
2086 try:
2087 s = os.read(p[0], 1)
2088 os.write(fd, s)
2089
2090 if s != '\b':
2091 try:
2092 num = num + s
2093 except:
2094 pass
2095 else:
2096 if num and len(num):
2097 l = string.split(num, '/')
2098 try:
2099 val = (int(l[0]) * 100) / int(l[1])
2100 except (IndexError, TypeError):
2101 pass
2102 else:
2103 w and w.set(val)
2104 num = ''
2105 except OSError, args:
2106 (errno, str) = args
2107 if (errno != 4):
2108 raise IOError, args
2109
2110 try:
2111 (pid, status) = os.waitpid(childpid, 0)
2112 except OSError, (num, msg):
2113 log.critical("exception from waitpid while formatting: %s %s" %(num, msg))
2114 status = None
2115 os.close(fd)
2116
2117 w and w.pop()
2118
2119 # *shrug* no clue why this would happen, but hope that things are fine
2120 if status is None:
2121 return 0
2122
2123 if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0):
2124 return 0
2125
2126 return 1
2127
2128 # copy and paste job from booty/bootloaderInfo.py...
2129 def getDiskPart(dev):
2130 cut = len(dev)
2131 if (dev.startswith('rd/') or dev.startswith('ida/') or
2132 dev.startswith('cciss/') or dev.startswith('sx8/') or
2133 dev.startswith('mapper/')):
2134 if dev[-2] == 'p':
2135 cut = -1
2136 elif dev[-3] == 'p':
2137 cut = -2
2138 else:
2139 if dev[-2] in string.digits:
2140 cut = -2
2141 elif dev[-1] in string.digits:
2142 cut = -1
2143
2144 name = dev[:cut]
2145
2146 # hack off the trailing 'p' from /dev/cciss/*, for example
2147 if name[-1] == 'p':
2148 for letter in name:
2149 if letter not in string.letters and letter != "/":
2150 name = name[:-1]
2151 break
2152
2153 if cut < 0:
2154 partNum = int(dev[cut:]) - 1
2155 else:
2156 partNum = None
2157
2158 return (name, partNum)