#
# fstab.py: filesystem management
#
# Matt Wilson <msw@redhat.com>
#
# Copyright 2001-2002 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# library public license.
#
# You should have received a copy of the GNU Library Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import string
import isys
import iutil
import os
import posix
import errno
import parted
import sys
import struct
import partitioning
import partedUtils
import raid
import lvm
import types
from rhpl.log import log
from rhpl.translate import _, N_
class BadBlocksError(Exception):
pass
defaultMountPoints = ['/', '/home', '/tmp', '/usr', '/var', '/usr/local', '/opt']
if iutil.getArch() == "s390":
# Many s390 have 2G DASDs, we recomment putting /usr/share on its own DASD
defaultMountPoints.insert(4, '/usr/share')
if iutil.getArch() == "ia64":
defaultMountPoints.insert(1, '/boot/efi')
else:
defaultMountPoints.insert(1, '/boot')
fileSystemTypes = {}
# XXX define availraidlevels and defaultmntpts as arch characteristics
availRaidLevels = raid.getRaidLevels()
def fileSystemTypeGetDefault():
if fileSystemTypeGet('ext3').isSupported():
return fileSystemTypeGet('ext3')
elif fileSystemTypeGet('ext2').isSupported():
return fileSystemTypeGet('ext2')
else:
raise ValueError, "You have neither ext3 or ext2 support in your kernel!"
def fileSystemTypeGet(key):
return fileSystemTypes[key]
def fileSystemTypeRegister(klass):
fileSystemTypes[klass.getName()] = klass
def fileSystemTypeGetTypes():
return fileSystemTypes.copy()
def getUsableLinuxFs():
rc = []
for fsType in fileSystemTypes.keys():
if fileSystemTypes[fsType].isMountable() and \
fileSystemTypes[fsType].isLinuxNativeFS():
rc.append(fsType)
# make sure the default is first in the list, kind of ugly
default = fileSystemTypeGetDefault()
defaultName = default.getName()
if defaultName in rc:
del rc[rc.index(defaultName)]
rc.insert(0, defaultName)
return rc
def mountCompare(a, b):
one = a.mountpoint
two = b.mountpoint
if one < two:
return -1
elif two > one:
return 1
return 0
def devify(device):
if device != "none" and device[0] != '/':
return "/dev/" + device
return device
class LabelFactory:
def __init__(self):
self.labels = None
def createLabel(self, mountpoint, maxLabelChars, kslabel = None):
if self.labels == None:
self.labels = {}
diskset = partedUtils.DiskSet()
diskset.openDevices()
diskset.stopAllRaid()
diskset.startAllRaid()
labels = diskset.getLabels()
del diskset
self.reserveLabels(labels)
# If a label was specified in the kickstart file, return that as
# the label - unless it's already in the reserved list. If that's
# the case, make a new one.
if kslabel and kslabel not in self.labels:
self.labels[kslabel] = 1
return kslabel
if len(mountpoint) > maxLabelChars:
mountpoint = mountpoint[0:maxLabelChars]
count = 0
while self.labels.has_key(mountpoint):
count = count + 1
s = "%s" % count
if (len(mountpoint) + len(s)) <= maxLabelChars:
mountpoint = mountpoint + s
else:
strip = len(mountpoint) + len(s) - maxLabelChars
mountpoint = mountpoint[0:len(mountpoint) - strip] + s
self.labels[mountpoint] = 1
return mountpoint
def reserveLabels(self, labels):
if self.labels == None:
self.labels = {}
for device, label in labels.items():
self.labels[label] = 1
labelFactory = LabelFactory()
class FileSystemType:
kernelFilesystems = {}
def __init__(self):
self.deviceArguments = {}
self.formattable = 0
self.checked = 0
self.name = ""
self.linuxnativefs = 0
self.partedFileSystemType = None
self.partedPartitionFlags = []
self.maxSizeMB = 8 * 1024 * 1024
self.supported = -1
self.defaultOptions = "defaults"
self.migratetofs = None
self.extraFormatArgs = []
self.maxLabelChars = 16
self.packages = []
def mount(self, device, mountpoint, readOnly=0, bindMount=0):
if not self.isMountable():
return
iutil.mkdirChain(mountpoint)
isys.mount(device, mountpoint, fstype = self.getName(),
readOnly = readOnly, bindMount = bindMount)
def umount(self, device, path):
isys.umount(path, removeDir = 0)
def getName(self):
return self.name
def getNeededPackages(self):
return self.packages
def registerDeviceArgumentFunction(self, klass, function):
self.deviceArguments[klass] = function
def badblocksDevice(self, entry, windowCreator, chroot='/'):
if windowCreator:
w = windowCreator(_("Checking for Bad Blocks"),
_("Checking for bad blocks on /dev/%s...")
% (entry.device.getDevice(),), 100)
else:
w = None
devicePath = entry.device.setupDevice(chroot)
args = [ "/usr/sbin/badblocks", "-vv", devicePath ]
# entirely too much cutting and pasting from ext2FormatFileSystem
fd = os.open("/dev/tty5", os.O_RDWR | os.O_CREAT | os.O_APPEND)
p = os.pipe()
childpid = os.fork()
if not childpid:
os.close(p[0])
os.dup2(p[1], 1)
os.dup2(p[1], 2)
os.close(p[1])
os.close(fd)
os.execv(args[0], args)
log("failed to exec %s", args)
os._exit(1)
os.close(p[1])
s = 'a'
while s and s != ':':
try:
s = os.read(p[0], 1)
except OSError, args:
(num, str) = args
if (num != 4):
raise IOError, args
os.write(fd, s)
num = ''
numbad = 0
while s:
try:
s = os.read(p[0], 1)
os.write(fd, s)
if s not in ['\b', '\n']:
try:
num = num + s
except:
pass
else:
if s == '\b':
if num:
l = string.split(num, '/')
val = (long(l[0]) * 100) / long(l[1])
w and w.set(val)
else:
try:
blocknum = long(num)
numbad = numbad + 1
except:
pass
if numbad > 0:
raise BadBlocksError
num = ''
except OSError, args:
(num, str) = args
if (num != 4):
raise IOError, args
try:
(pid, status) = os.waitpid(childpid, 0)
except OSError, (num, msg):
log("exception from waitpid in badblocks: %s %s" % (num, msg))
status = None
os.close(fd)
w and w.pop()
if numbad > 0:
raise BadBlocksError
# have no clue how this would happen, but hope we're okay
if status is None:
return
if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0):
return
raise SystemError
def formatDevice(self, entry, progress, chroot='/'):
if self.isFormattable():
raise RuntimeError, "formatDevice method not defined"
def migrateFileSystem(self, device, message, chroot='/'):
if self.isMigratable():
raise RuntimeError, "migrateFileSystem method not defined"
def labelDevice(self, entry, chroot):
pass
def isFormattable(self):
return self.formattable
def isLinuxNativeFS(self):
return self.linuxnativefs
def readProcFilesystems(self):
f = open("/proc/filesystems", 'r')
if not f:
pass
lines = f.readlines()
for line in lines:
fields = string.split(line)
if fields[0] == "nodev":
fsystem = fields[1]
else:
fsystem = fields[0]
FileSystemType.kernelFilesystems[fsystem] = None
def isMountable(self):
if not FileSystemType.kernelFilesystems:
self.readProcFilesystems()
return FileSystemType.kernelFilesystems.has_key(self.getName())
def isSupported(self):
if self.supported == -1:
return self.isMountable()
return self.supported
def isChecked(self):
return self.checked
def getDeviceArgs(self, device):
deviceArgsFunction = self.deviceArguments.get(device.__class__)
if not deviceArgsFunction:
return []
return deviceArgsFunction(device)
def getPartedFileSystemType(self):
return self.partedFileSystemType
def getPartedPartitionFlags(self):
return self.partedPartitionFlags
# note that this returns the maximum size of a filesystem in megabytes
def getMaxSizeMB(self):
return self.maxSizeMB
def getDefaultOptions(self, mountpoint):
return self.defaultOptions
def getMigratableFSTargets(self):
retval = []
if not self.migratetofs:
return retval
for fs in self.migratetofs:
if fileSystemTypeGet(fs).isSupported():
retval.append(fs)
return retval
def isMigratable(self):
if len(self.getMigratableFSTargets()) > 0:
return 1
else:
return 0
class reiserfsFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("reiserfs")
self.formattable = 1
self.checked = 1
self.linuxnativefs = 1
# this is totally, 100% unsupported. Boot with "linux reiserfs"
# at the boot: prompt will let you make new reiserfs filesystems
# in the installer. Bugs filed when you use this will be closed
# WONTFIX.
try:
f = open("/proc/cmdline")
line = f.readline()
if string.find(line, " reiserfs") != -1:
self.supported = -1
else:
self.supported = 0
del f
except:
self.supported = 0
self.name = "reiserfs"
self.packages = [ "reiserfs-utils" ]
self.maxSizeMB = 8 * 1024 * 1024
def formatDevice(self, entry, progress, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
p = os.pipe()
os.write(p[1], "y\n")
os.close(p[1])
rc = iutil.execWithRedirect("/usr/sbin/mkreiserfs",
["mkreiserfs", devicePath ],
stdin = p[0],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
fileSystemTypeRegister(reiserfsFileSystem())
class xfsFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("xfs")
self.formattable = 1
self.checked = 1
self.linuxnativefs = 1
self.name = "xfs"
self.maxSizeMB = 16 * 1024 * 1024
self.maxLabelChars = 12
# this is totally, 100% unsupported. Boot with "linux xfs"
# at the boot: prompt will let you make new xfs filesystems
# in the installer. Bugs filed when you use this will be closed
# WONTFIX.
try:
f = open("/proc/cmdline")
line = f.readline()
if string.find(line, " xfs") != -1:
self.supported = -1
else:
self.supported = 0
del f
except:
self.supported = 0
self.packages = [ "xfsprogs" ]
def formatDevice(self, entry, progress, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
rc = iutil.execWithRedirect("/usr/sbin/mkfs.xfs",
["mkfs.xfs", "-f", "-l", "internal",
devicePath ],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
def labelDevice(self, entry, chroot):
devicePath = entry.device.setupDevice(chroot)
label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars,
kslabel = entry.label)
db_cmd = "label " + label
rc = iutil.execWithRedirect("/usr/sbin/xfs_db",
["xfs_db", "-x", "-c", db_cmd,
devicePath],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
entry.setLabel(label)
fileSystemTypeRegister(xfsFileSystem())
class jfsFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("jfs")
self.formattable = 1
self.checked = 1
self.linuxnativefs = 1
self.maxLabelChars = 16
# this is totally, 100% unsupported. Boot with "linux jfs"
# at the boot: prompt will let you make new reiserfs filesystems
# in the installer. Bugs filed when you use this will be closed
# WONTFIX.
try:
f = open("/proc/cmdline")
line = f.readline()
if string.find(line, " jfs") != -1:
self.supported = -1
else:
self.supported = 0
del f
except:
self.supported = 0
if not os.access("/usr/sbin/mkfs.jfs", os.X_OK):
self.supported = 0
self.name = "jfs"
self.packages = [ "jfsutils" ]
self.maxSizeMB = 8 * 1024 * 1024
def labelDevice(self, entry, chroot):
devicePath = entry.device.setupDevice(chroot)
label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars,
kslabel = entry.label)
rc = iutil.execWithRedirect("/usr/sbin/jfs_tune",
["jfs_tune", "-L", label, devicePath],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
entry.setLabel(label)
def formatDevice(self, entry, progress, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
rc = iutil.execWithRedirect("/usr/sbin/mkfs.jfs",
["mkfs.jfs", "-q",
devicePath ],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
fileSystemTypeRegister(jfsFileSystem())
class extFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = None
self.formattable = 1
self.checked = 1
self.linuxnativefs = 1
self.maxSizeMB = 8 * 1024 * 1024
self.packages = [ "e2fsprogs" ]
def labelDevice(self, entry, chroot):
devicePath = entry.device.setupDevice(chroot)
label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars,
kslabel = entry.label)
rc = iutil.execWithRedirect("/usr/sbin/e2label",
["e2label", devicePath, label],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
entry.setLabel(label)
def formatDevice(self, entry, progress, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
devArgs = self.getDeviceArgs(entry.device)
args = [ "/usr/sbin/mke2fs", devicePath]
args.extend(devArgs)
args.extend(self.extraFormatArgs)
rc = ext2FormatFilesystem(args, "/dev/tty5",
progress,
entry.mountpoint)
if rc:
raise SystemError
# this is only for ext3 filesystems, but migration is a method
# of the ext2 fstype, so it needs to be here. FIXME should be moved
def setExt3Options(self, entry, message, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
# if no journal, don't turn off the fsck
if not isys.ext2HasJournal(devicePath, makeDevNode = 0):
return
rc = iutil.execWithRedirect("/usr/sbin/tune2fs",
["tunefs", "-c0", "-i0", "-Odir_index",
devicePath],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
class ext2FileSystem(extFileSystem):
def __init__(self):
extFileSystem.__init__(self)
self.name = "ext2"
self.partedFileSystemType = parted.file_system_type_get("ext2")
self.migratetofs = ['ext3']
def migrateFileSystem(self, entry, message, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
if not entry.fsystem or not entry.origfsystem:
raise RuntimeError, ("Trying to migrate fs w/o fsystem or "
"origfsystem set")
if entry.fsystem.getName() != "ext3":
raise RuntimeError, ("Trying to migrate ext2 to something other "
"than ext3")
# if journal already exists skip
if isys.ext2HasJournal(devicePath, makeDevNode = 0):
log("Skipping migration of %s, has a journal already.\n" % devicePath)
return
rc = iutil.execWithRedirect("/usr/sbin/tune2fs",
["tune2fs", "-j", devicePath ],
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
# XXX this should never happen, but appears to have done
# so several times based on reports in bugzilla.
# At least we can avoid leaving them with a system which won't boot
if not isys.ext2HasJournal(devicePath, makeDevNode = 0):
log("Migration of %s attempted but no journal exists after "
"running tune2fs.\n" % (devicePath))
if message:
rc = message(_("Error"),
_("An error occurred migrating %s to ext3. It is "
"possible to continue without migrating this "
"file system if desired.\n\n"
"Would you like to continue without migrating %s?")
% (devicePath, devicePath), type = "yesno")
if rc == 0:
sys.exit(0)
entry.fsystem = entry.origfsystem
else:
extFileSystem.setExt3Options(self, entry, message, chroot)
fileSystemTypeRegister(ext2FileSystem())
class ext3FileSystem(extFileSystem):
def __init__(self):
extFileSystem.__init__(self)
self.name = "ext3"
self.extraFormatArgs = [ "-j" ]
self.partedFileSystemType = parted.file_system_type_get("ext3")
def formatDevice(self, entry, progress, chroot='/'):
extFileSystem.formatDevice(self, entry, progress, chroot)
extFileSystem.setExt3Options(self, entry, progress, chroot)
fileSystemTypeRegister(ext3FileSystem())
class raidMemberDummyFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("ext2")
self.partedPartitionFlags = [ parted.PARTITION_RAID ]
self.formattable = 1
self.checked = 0
self.linuxnativefs = 1
self.name = "software RAID"
self.maxSizeMB = 8 * 1024 * 1024
self.supported = 1
if len(availRaidLevels) == 0:
self.supported = 0
self.packages = [ "mdadm" ]
def formatDevice(self, entry, progress, chroot='/'):
# mkraid did all we need to format this partition...
pass
fileSystemTypeRegister(raidMemberDummyFileSystem())
class lvmPhysicalVolumeDummyFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("ext2")
self.partedPartitionFlags = [ parted.PARTITION_LVM ]
self.formattable = 1
self.checked = 0
self.linuxnativefs = 1
self.name = "physical volume (LVM)"
self.maxSizeMB = 8 * 1024 * 1024
self.supported = 1
self.packages = [ "lvm2" ]
def isMountable(self):
return 0
def formatDevice(self, entry, progress, chroot='/'):
# already done by the pvcreate during volume creation
pass
fileSystemTypeRegister(lvmPhysicalVolumeDummyFileSystem())
class lvmVolumeGroupDummyFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("ext2")
self.formattable = 1
self.checked = 0
self.linuxnativefs = 0
self.name = "volume group (LVM)"
self.supported = 0
self.maxSizeMB = 8 * 1024 * 1024
self.packages = [ "lvm2" ]
def isMountable(self):
return 0
def formatDevice(self, entry, progress, chroot='/'):
# the vgcreate already did this
pass
fileSystemTypeRegister(lvmVolumeGroupDummyFileSystem())
class swapFileSystem(FileSystemType):
enabledSwaps = {}
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("linux-swap")
self.formattable = 1
self.name = "swap"
self.maxSizeMB = 8 * 1024 * 1024
self.linuxnativefs = 1
self.supported = 1
def mount(self, device, mountpoint, readOnly=0, bindMount=0):
pagesize = isys.getpagesize()
buf = None
if pagesize > 2048:
num = pagesize
else:
num = 2048
try:
fd = os.open(dev, os.O_RDONLY)
buf = os.read(fd, num)
os.close(fd)
except:
pass
# FIXME: we should ask if they want to reinitialize swaps that
# are of format 0 (#122101)
if buf is not None and len(buf) == pagesize:
if buf[pagesize - 10:] == "SWAP-SPACE":
log("SWAP is of format 0, skipping it")
return
isys.swapon (device)
def umount(self, device, path):
# unfortunately, turning off swap is bad.
raise RuntimeError, "unable to turn off swap"
def formatDevice(self, entry, progress, chroot='/'):
file = entry.device.setupDevice(chroot)
rc = iutil.execWithRedirect ("/usr/sbin/mkswap",
[ "mkswap", '-v1', file ],
stdout = "/dev/tty5",
stderr = "/dev/tty5",
searchPath = 1)
if rc:
raise SystemError
def labelDevice(self, entry, chroot):
file = entry.device.setupDevice(chroot)
devName = entry.device.getDevice()
# we'll keep the SWAP-* naming for all devs but Compaq SMART2
# nodes (#170500)
if devName[0:6] == "cciss/":
swapLabel = "SW-%s" % (devName)
else:
swapLabel = "SWAP-%s" % (devName)
label = labelFactory.createLabel("%s" %swapLabel, self.maxLabelChars)
rc = iutil.execWithRedirect ("/usr/sbin/mkswap",
[ "mkswap", '-v1', "-L", label, file ],
stdout = "/dev/tty5",
stderr = "/dev/tty5",
searchPath = 1)
if rc:
raise SystemError
entry.setLabel(label)
fileSystemTypeRegister(swapFileSystem())
class FATFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("fat32")
self.formattable = 1
self.supported = 1
self.checked = 0
self.maxSizeMB = 1024 * 1024
self.name = "vfat"
self.packages = [ "dosfstools" ]
self.maxLabelChars = 11
self.migratetofs = ['vfat']
def formatDevice(self, entry, progress, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
devArgs = self.getDeviceArgs(entry.device)
args = [ "mkdosfs", devicePath ]
args.extend(devArgs)
rc = iutil.execWithRedirect("/usr/sbin/mkdosfs", args,
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
def labelDevice(self, entry, chroot):
if not iutil.getArch() == 'ia64':
return
devicePath = entry.device.setupDevice(chroot)
label = labelFactory.createLabel(entry.mountpoint, self.maxLabelChars,
kslabel = entry.label)
rc = iutil.execWithRedirect("/usr/sbin/dosfslabel",
["dosfslabel", devicePath, label],
stdout = "/dev/tty5",
stderr = "/dev/tty5",
searchPath = 1)
newLabel = iutil.execWithCapture("/usr/sbin/dosfslabel",
["dosfslabel", devicePath],
stderr = "/dev/tty5")
newLabel = newLabel.strip()
if label != newLabel:
raise SystemError, "dosfslabel failed on device %s" % (devicePath,)
entry.setLabel(label)
def _readFstab(self, path):
f = open (path, "r")
lines = f.readlines ()
f.close()
fstab = []
for line in lines:
fields = string.split(line)
if not fields:
fstab.append(line)
continue
if line[0] == "#":
fstab.append(line)
# skip all comments
continue
# all valid fstab entries have 6 fields; if the last two are
# missing they are assumed to be zero per fstab(5)
if len(fields) < 4:
fstab.append(line)
continue
elif len(fields) == 4:
fields.append(0)
fields.append(0)
elif len(fields) == 5:
fields.append(0)
elif len(fields) > 6:
fstab.append(line)
continue
fstab.append(fields)
return fstab
def migrateFileSystem(self, entry, message, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
if not entry.fsystem or not entry.origfsystem:
raise RuntimeError, ("Trying to migrate fs w/o fsystem or "
"origfsystem set")
if entry.fsystem.getName() != "vfat":
raise RuntimeError, ("Trying to migrate vfat to something other "
"than vfat")
self.labelDevice(entry, chroot)
if not entry.label:
return
try:
os.stat(chroot + "/etc/fstab")
except:
return
mounts = self._readFstab(chroot + "/etc/fstab")
changed = False
for mount in mounts:
if type(mount) == types.ListType:
if mount[0] == "/dev/%s" % (entry.device.getDevice(),):
mount[0] = "LABEL=%s" % (entry.label,)
changed = True
if changed:
os.rename(chroot + "/etc/fstab", chroot + "/etc/fstab.anaconda")
f = open (chroot + "/etc/fstab", "w")
for mount in mounts:
if type(mount) == types.ListType:
mount = string.join(mount, "\t")
if mount[:-1] != "\n":
mount += "\n"
f.write(mount)
f.close()
fileSystemTypeRegister(FATFileSystem())
class NTFSFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("ntfs")
self.formattable = 0
self.checked = 0
self.name = "ntfs"
fileSystemTypeRegister(NTFSFileSystem())
class hfsFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = parted.file_system_type_get("hfs")
self.formattable = 1
self.checked = 0
self.name = "hfs"
self.supported = 0
def isMountable(self):
return 0
def formatDevice(self, entry, progress, chroot='/'):
devicePath = entry.device.setupDevice(chroot)
devArgs = self.getDeviceArgs(entry.device)
args = [ "hformat", devicePath ]
args.extend(devArgs)
rc = iutil.execWithRedirect("/usr/bin/hformat", args,
stdout = "/dev/tty5",
stderr = "/dev/tty5")
if rc:
raise SystemError
fileSystemTypeRegister(hfsFileSystem())
class applebootstrapFileSystem(hfsFileSystem):
def __init__(self):
hfsFileSystem.__init__(self)
self.partedPartitionFlags = [ parted.PARTITION_BOOT ]
self.maxSizeMB = 1
self.name = "Apple Bootstrap"
if iutil.getPPCMacGen() == "NewWorld":
self.supported = 1
else:
self.supported = 0
fileSystemTypeRegister(applebootstrapFileSystem())
class prepbootFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.partedFileSystemType = None
self.partedPartitionFlags = [ parted.PARTITION_BOOT ]
self.checked = 0
self.name = "PPC PReP Boot"
self.maxSizeMB = 10
if iutil.getPPCMachine() == "iSeries":
self.maxSizeMB = 64
# supported for use on the pseries
if (iutil.getPPCMachine() == "pSeries" or
iutil.getPPCMachine() == "iSeries"):
self.supported = 1
self.formattable = 1
else:
self.supported = 0
self.formattable = 0
def formatDevice(self, entry, progress, chroot='/'):
# copy and paste job from booty/bootloaderInfo.py...
def getDiskPart(dev):
cut = len(dev)
if (dev.startswith('rd/') or dev.startswith('ida/') or
dev.startswith('cciss/') or dev.startswith('i2o/')
or dev.startswith("sx8/")):
if dev[-2] == 'p':
cut = -1
elif dev[-3] == 'p':
cut = -2
else:
if dev[-2] in string.digits:
cut = -2
elif dev[-1] in string.digits:
cut = -1
name = dev[:cut]
# hack off the trailing 'p' from /dev/cciss/*, for example
if name[-1] == 'p':
for letter in name:
if letter not in string.letters and letter != "/":
name = name[:-1]
break
if cut < 0:
partNum = int(dev[cut:])
else:
partNum = None
return (name, partNum)
# FIXME: oh dear is this a hack beyond my wildest imagination.
# parted doesn't really know how to do these, so we're going to
# exec sfdisk and make it set the partition type. this is bloody
# ugly
devicePath = entry.device.setupDevice(chroot)
(disk, part) = getDiskPart(devicePath)
if disk is None or part is None:
log("oops, somehow got a bogus device for the PReP partition "
"(%s)" %(devicePath,))
return
args = [ "sfdisk", "--change-id", disk, "%d" %(part,), "41" ]
if disk.startswith("/tmp/") and not os.access(disk, os.R_OK):
isys.makeDevInode(disk[5:], disk)
log("going to run %s" %(args,))
rc = iutil.execWithRedirect("/usr/sbin/sfdisk", args,
stdout = "/dev/tty5", stderr = "/dev/tty5")
if rc:
raise SystemError
fileSystemTypeRegister(prepbootFileSystem())
class ForeignFileSystem(FileSystemType):
def __init__(self):
FileSystemType.__init__(self)
self.formattable = 0
self.checked = 0
self.name = "foreign"
def formatDevice(self, entry, progress, chroot='/'):
return
fileSystemTypeRegister(ForeignFileSystem())
class PsudoFileSystem(FileSystemType):
def __init__(self, name):
FileSystemType.__init__(self)
self.formattable = 0
self.checked = 0
self.name = name
self.supported = 0
class ProcFileSystem(PsudoFileSystem):
def __init__(self):
PsudoFileSystem.__init__(self, "proc")
fileSystemTypeRegister(ProcFileSystem())
class SysfsFileSystem(PsudoFileSystem):
def __init__(self):
PsudoFileSystem.__init__(self, "sysfs")
fileSystemTypeRegister(SysfsFileSystem())
class SelinuxfsFileSystem(PsudoFileSystem):
def __init__(self):
PsudoFileSystem.__init__(self, "selinuxfs")
fileSystemTypeRegister(SelinuxfsFileSystem())
class DevptsFileSystem(PsudoFileSystem):
def __init__(self):
PsudoFileSystem.__init__(self, "devpts")
self.defaultOptions = "gid=5,mode=620"
def isMountable(self):
return 0
fileSystemTypeRegister(DevptsFileSystem())
class DevshmFileSystem(PsudoFileSystem):
def __init__(self):
PsudoFileSystem.__init__(self, "tmpfs")
def isMountable(self):
return 0
fileSystemTypeRegister(DevshmFileSystem())
class AutoFileSystem(PsudoFileSystem):
def __init__(self):
PsudoFileSystem.__init__(self, "auto")
fileSystemTypeRegister(AutoFileSystem())
class BindFileSystem(AutoFileSystem):
def __init__(self):
PsudoFileSystem.__init__(self, "bind")
def isMountable(self):
return 1
fileSystemTypeRegister(BindFileSystem())
class FileSystemSet:
def __init__(self):
self.messageWindow = None
self.progressWindow = None
self.waitWindow = None
self.mountcount = 0
self.migratedfs = 0
self.reset()
self.volumesCreated = 0
def isActive(self):
return self.mountcount != 0
def registerMessageWindow(self, method):
self.messageWindow = method
def registerProgressWindow(self, method):
self.progressWindow = method
def registerWaitWindow(self, method):
self.waitWindow = method
def reset (self):
self.entries = []
proc = FileSystemSetEntry(Device(), '/proc', fileSystemTypeGet("proc"))
self.add(proc)
sys = FileSystemSetEntry(Device(), '/sys', fileSystemTypeGet("sysfs"))
self.add(sys)
pts = FileSystemSetEntry(Device(), '/dev/pts',
fileSystemTypeGet("devpts"), "gid=5,mode=620")
self.add(pts)
shm = FileSystemSetEntry(Device(), '/dev/shm', fileSystemTypeGet("tmpfs"))
self.add(shm)
def verify (self):
for entry in self.entries:
if type(entry.__dict__) != type({}):
raise RuntimeError, "fsset internals inconsistent"
def add (self, entry):
# remove any existing duplicate entries
for existing in self.entries:
if (existing.device.getDevice() == entry.device.getDevice()
and existing.mountpoint == entry.mountpoint):
self.remove(existing)
# XXX debuggin'
## log ("fsset at %s\n"
## "adding entry for %s\n"
## "entry object %s, class __dict__ is %s",
## self, entry.mountpoint, entry,
## isys.printObject(entry.__dict__))
self.entries.append(entry)
self.entries.sort (mountCompare)
def remove (self, entry):
self.entries.remove(entry)
def getEntryByMountPoint(self, mount):
for entry in self.entries:
if entry.mountpoint == mount:
return entry
return None
def getEntryByDeviceName(self, dev):
for entry in self.entries:
if entry.device.getDevice() == dev:
return entry
return None
def copy (self):
new = FileSystemSet()
for entry in self.entries:
new.add (entry)
return new
def fstab (self):
format = "%-23s %-23s %-7s %-15s %d %d\n"
fstab = ""
for entry in self.entries:
if entry.mountpoint:
if entry.getLabel():
device = "LABEL=%s" % (entry.getLabel(),)
else:
device = devify(entry.device.getDevice())
fstab = fstab + entry.device.getComment()
fstab = fstab + format % (device, entry.mountpoint,
entry.fsystem.getName(),
entry.options, entry.fsck,
entry.order)
return fstab
def mtab (self):
format = "%s %s %s %s 0 0\n"
mtab = ""
for entry in self.entries:
if not entry.isMounted():
continue
if entry.mountpoint:
# swap doesn't end up in the mtab
if entry.fsystem.getName() == "swap":
continue
if entry.options:
options = "rw," + entry.options
else:
options = "rw"
mtab = mtab + format % (devify(entry.device.getDevice()),
entry.mountpoint,
entry.fsystem.getName(),
options)
return mtab
def raidtab(self):
# set up raidtab...
raidtab = ""
for entry in self.entries:
if entry.device.getName() == "RAIDDevice":
raidtab = raidtab + entry.device.raidTab()
return raidtab
def mdadmConf(self):
raident = 0
cf = """
# mdadm.conf written out by anaconda
DEVICE partitions
"""
for ent in self.entries:
if ent.device.getName() != "RAIDDevice":
continue
raident +=1
cf = cf + ent.device.mdadmLine()
if raident > 0:
return cf
return
def write (self, prefix):
f = open (prefix + "/etc/fstab", "w")
f.write (self.fstab())
f.close ()
cf = self.mdadmConf()
if cf:
f = open (prefix + "/etc/mdadm.conf", "w")
f.write (cf)
f.close ()
# touch mtab
open (prefix + "/etc/mtab", "w+")
f.close ()
def restoreMigratedFstab(self, prefix):
if not self.migratedfs:
return
fname = prefix + "/etc/fstab"
if os.access(fname + ".rpmsave", os.R_OK):
os.rename(fname + ".rpmsave", fname)
def migratewrite(self, prefix):
if not self.migratedfs:
return
fname = prefix + "/etc/fstab"
f = open (fname, "r")
lines = f.readlines()
f.close()
perms = os.stat(fname)[0] & 0777
os.rename(fname, fname + ".rpmsave")
f = open (fname, "w+")
os.chmod(fname, perms)
for line in lines:
fields = string.split(line)
# try to be smart like in fsset.py::readFstab()
if not fields or line[0] == "#":
f.write(line)
continue
if len (fields) < 4 or len (fields) > 6:
f.write(line)
continue
if string.find(fields[3], "noauto") != -1:
f.write(line)
continue
mntpt = fields[1]
entry = self.getEntryByMountPoint(mntpt)
if not entry or not entry.getMigrate():
f.write(line)
elif entry.origfsystem.getName() != fields[2]:
f.write(line)
else:
fields[2] = entry.fsystem.getName()
newline = "%-23s %-23s %-7s %-15s %s %s\n" % (fields[0],
fields[1],
fields[2],
fields[3],
fields[4],
fields[5])
f.write(newline)
f.close()
# return the "boot" device
def getBootDev(self):
mntDict = {}
bootDev = None
for entry in self.entries:
mntDict[entry.mountpoint] = entry.device
# FIXME: this ppc stuff feels kind of crufty -- the abstraction
# here needs a little bit of work
if iutil.getPPCMacGen() == "NewWorld":
for entry in self.entries:
if entry.fsystem.getName() == "Apple Bootstrap":
bootDev = entry.device
elif (iutil.getPPCMachine() == "pSeries" or
iutil.getPPCMachine() == "iSeries"):
# we want the first prep partition or the first newly formatted one
bestprep = None
for entry in self.entries:
if ((entry.fsystem.getName() == "PPC PReP Boot")
and ((bestprep is None) or
((bestprep.format == 0) and (entry.format == 1)))):
bestprep = entry
if bestprep:
bootDev = bestprep.device
elif iutil.getArch() == "ia64":
if mntDict.has_key("/boot/efi"):
bootDev = mntDict['/boot/efi']
elif mntDict.has_key("/boot"):
bootDev = mntDict['/boot']
else:
bootDev = mntDict['/']
return bootDev
def bootloaderChoices(self, diskSet, bl):
ret = {}
bootDev = self.getBootDev()
if bootDev is None:
log("no boot device set")
return ret
if bootDev.getName() == "RAIDDevice":
ret['boot'] = (bootDev.device, N_("RAID Device"))
ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
return ret
if iutil.getPPCMacGen() == "NewWorld":
ret['boot'] = (bootDev.device, N_("Apple Bootstrap"))
n = 1
for entry in self.entries:
if ((entry.fsystem.getName() == "Apple Bootstrap") and (
entry.device.getDevice() != bootDev.device)):
ret['boot%d' %(n,)] = (entry.device.getDevice(),
N_("Apple Bootstrap"))
n = n + 1
return ret
elif (iutil.getPPCMachine() == "pSeries" or
iutil.getPPCMachine() == "iSeries"):
ret['boot'] = (bootDev.device, N_("PPC PReP Boot"))
return ret
ret['boot'] = (bootDev.device, N_("First sector of boot partition"))
try:
# we won't have this on zFCP-only zSeries systems
ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)"))
except:
pass
return ret
# set active partition on disks
# if an active partition is set, leave it alone; if none set
# set either our boot partition or the first partition on the drive active
def setActive(self, diskset):
dev = self.getBootDev()
if dev is None:
return
bootDev = dev.device
# on ia64, *only* /boot/efi should be marked bootable
# similarly, on pseries, we really only want the PReP partition active
if (iutil.getArch() == "ia64" or iutil.getPPCMachine() == "pSeries"
or iutil.getPPCMachine() == "iSeries"):
part = partedUtils.get_partition_by_name(diskset.disks, bootDev)
if part and part.is_flag_available(parted.PARTITION_BOOT):
part.set_flag(parted.PARTITION_BOOT, 1)
return
for drive in diskset.disks.keys():
foundActive = 0
bootPart = None
disk = diskset.disks[drive]
part = disk.next_partition()
while part:
if not part.is_active():
part = disk.next_partition(part)
continue
if not part.is_flag_available(parted.PARTITION_BOOT):
foundActive = 1
part = None
continue
if part.get_flag(parted.PARTITION_BOOT):
foundActive = 1
part = None
continue
if not bootPart:
bootPart = part
if partedUtils.get_partition_name(part) == bootDev:
bootPart = part
part = disk.next_partition(part)
if bootPart and not foundActive:
bootPart.set_flag(parted.PARTITION_BOOT, 1)
if bootPart:
del bootPart
def formatSwap (self, chroot):
formatted = []
notformatted = []
for entry in self.entries:
if (not entry.fsystem or not entry.fsystem.getName() == "swap" or
entry.isMounted()):
continue
if not entry.getFormat():
notformatted.append(entry)
continue
try:
self.formatEntry(entry, chroot)
formatted.append(entry)
except SystemError:
if self.messageWindow:
self.messageWindow(_("Error"),
_("An error occurred trying to "
"initialize swap on device %s. This "
"problem is serious, and the install "
"cannot continue.\n\n"
"Press <Enter> to reboot your system.")
% (entry.device.getDevice(),))
sys.exit(0)
for entry in formatted:
try:
self.labelEntry(entry, chroot)
except SystemError:
# should be OK, fall back to by device
pass
# find if there's a label on the ones we're not formatting
for entry in notformatted:
dev = entry.device.getDevice()
if not dev or dev == "none":
continue
try:
label = isys.readFSLabel(dev)
except:
continue
if label:
entry.setLabel(label)
def turnOnSwap (self, chroot):
for entry in self.entries:
if (entry.fsystem and entry.fsystem.getName() == "swap"
and not entry.isMounted()):
try:
entry.mount(chroot)
self.mountcount = self.mountcount + 1
except SystemError, (num, msg):
if self.messageWindow:
self.messageWindow(_("Error"),
_("Error enabling swap device %s: "
"%s\n\n"
"This most likely means this "
"swap partition has not been "
"initialized."
"\n\n"
"Press OK to reboot your "
"system.")
% (entry.device.getDevice(), msg))
sys.exit(0)
def labelEntry(self, entry, chroot):
label = entry.device.getLabel()
if label:
entry.setLabel(label)
elif entry.device.doLabel is not None:
entry.fsystem.labelDevice(entry, chroot)
def formatEntry(self, entry, chroot):
log("formatting %s as %s" %(entry.mountpoint, entry.fsystem.name))
entry.fsystem.formatDevice(entry, self.progressWindow, chroot)
def badblocksEntry(self, entry, chroot):
entry.fsystem.badblocksDevice(entry, self.progressWindow, chroot)
def getMigratableEntries(self):
retval = []
for entry in self.entries:
if entry.origfsystem and entry.origfsystem.isMigratable():
retval.append(entry)
return retval
def formattablePartitions(self):
list = []
for entry in self.entries:
if entry.fsystem.isFormattable():
list.append (entry)
return list
def checkBadblocks(self, chroot='/'):
for entry in self.entries:
if (not entry.fsystem.isFormattable() or not entry.getBadblocks()
or entry.isMounted()):
continue
try:
self.badblocksEntry(entry, chroot)
except BadBlocksError:
log("Bad blocks detected on device %s",entry.device.getDevice())
if self.messageWindow:
self.messageWindow(_("Error"),
_("Bad blocks have been detected on "
"device /dev/%s. We do "
"not recommend you use this device."
"\n\n"
"Press <Enter> to reboot your system") %
(entry.device.getDevice(),))
sys.exit(0)
except SystemError:
if self.messageWindow:
self.messageWindow(_("Error"),
_("An error occurred searching for "
"bad blocks on %s. This problem is "
"serious, and the install cannot "
"continue.\n\n"
"Press <Enter> to reboot your system.")
% (entry.device.getDevice(),))
sys.exit(0)
def createLogicalVolumes (self, chroot='/'):
# first set up the volume groups
for entry in self.entries:
if entry.fsystem.name == "volume group (LVM)":
entry.device.setupDevice(chroot)
# then set up the logical volumes
for entry in self.entries:
if isinstance(entry.device, LogicalVolumeDevice):
entry.device.setupDevice(chroot)
self.volumesCreated = 1
def createBootRaid (self, chroot='/'):
bootDev = self.getBootDev()
if bootDev.getDevice().startswith('md'):
bootDev.setupDevice(chroot)
def makeFilesystems (self, chroot='/'):
formatted = []
notformatted = []
for entry in self.entries:
if (not entry.fsystem.isFormattable() or not entry.getFormat()
or entry.isMounted()):
notformatted.append(entry)
continue
try:
self.formatEntry(entry, chroot)
formatted.append(entry)
except SystemError:
if self.messageWindow:
self.messageWindow(_("Error"),
_("An error occurred trying to "
"format %s. This problem is "
"serious, and the install cannot "
"continue.\n\n"
"Press <Enter> to reboot your system.")
% (entry.device.getDevice(),))
sys.exit(0)
for entry in formatted:
try:
self.labelEntry(entry, chroot)
except SystemError:
# should be OK, we'll still use the device name to mount.
pass
# go through and have labels for the ones we don't format
for entry in notformatted:
dev = entry.device.getDevice()
if not dev or dev == "none":
continue
if not entry.mountpoint or entry.mountpoint == "swap":
continue
try:
label = isys.readFSLabel(dev)
except:
continue
if label:
entry.setLabel(label)
else:
self.labelEntry(entry, chroot)
def haveMigratedFilesystems(self):
return self.migratedfs
def migrateFilesystems (self, chroot='/'):
if self.migratedfs:
return
for entry in self.entries:
if not entry.origfsystem:
continue
if not entry.origfsystem.isMigratable() or not entry.getMigrate():
continue
try:
entry.origfsystem.migrateFileSystem(entry, self.messageWindow,
chroot)
except SystemError:
if self.messageWindow:
self.messageWindow(_("Error"),
_("An error occurred trying to "
"migrate %s. This problem is "
"serious, and the install cannot "
"continue.\n\n"
"Press <Enter> to reboot your system.")
% (entry.device.getDevice(),))
sys.exit(0)
self.migratedfs = 1
def mountFilesystems(self, instPath = '/', raiseErrors = 0, readOnly = 0):
for entry in self.entries:
if not entry.fsystem.isMountable():
continue
try:
log("trying to mount %s on %s" %(entry.device.getDevice(), entry.mountpoint))
entry.mount(instPath, readOnly = readOnly)
self.mountcount = self.mountcount + 1
except OSError, (num, msg):
if self.messageWindow:
if num == errno.EEXIST:
self.messageWindow(_("Invalid mount point"),
_("An error occurred when trying "
"to create %s. Some element of "
"this path is not a directory. "
"This is a fatal error and the "
"install cannot continue.\n\n"
"Press <Enter> to reboot your "
"system.") % (entry.mountpoint,))
else:
self.messageWindow(_("Invalid mount point"),
_("An error occurred when trying "
"to create %s: %s. This is "
"a fatal error and the install "
"cannot continue.\n\n"
"Press <Enter> to reboot your "
"system.") % (entry.mountpoint,
msg))
sys.exit(0)
except SystemError, (num, msg):
if raiseErrors:
raise SystemError, (num, msg)
if self.messageWindow:
self.messageWindow(_("Error"),
_("Error mounting device %s as %s: "
"%s\n\n"
"This most likely means this "
"partition has not been formatted."
"\n\n"
"Press OK to reboot your system.")
% (entry.device.getDevice(),
entry.mountpoint, msg))
sys.exit(0)
self.makeLVMNodes(instPath)
def makeLVMNodes(self, instPath, trylvm1 = 0):
# XXX hack to make the device node exist for the root fs if
# it's a logical volume so that mkinitrd can create the initrd.
root = self.getEntryByMountPoint("/")
rootlvm1 = 0
if trylvm1:
dev = root.device.getDevice()
# lvm1 major is 58
if os.access("%s/dev/%s" %(instPath, dev), os.R_OK) and posix.major(os.stat("%s/dev/%s" %(instPath, dev)).st_rdev) == 58:
rootlvm1 = 1
if isinstance(root.device, LogicalVolumeDevice) or rootlvm1:
# now make sure all of the device nodes exist. *sigh*
rc = iutil.execWithRedirect("lvm",
["lvm", "vgmknodes", "-v"],
stdout = "/tmp/lvmout",
stderr = "/tmp/lvmout",
searchPath = 1)
rootDev = "/dev/%s" % (root.device.getDevice(),)
rootdir = instPath + rootDev[:string.rfind(rootDev, "/")]
if not os.path.exists(instPath + "/dev/mapper/control"):
iutil.makeDMNode(root=instPath)
if not os.path.isdir(rootdir):
os.makedirs(rootdir)
dmdev = "/dev/mapper/" + root.device.getDevice().replace("/", "-")
if os.path.exists(instPath + dmdev):
os.unlink(instPath + dmdev)
iutil.copyDeviceNode(dmdev, instPath + dmdev)
# unlink existing so that we dtrt on upgrades
if os.path.exists(instPath + rootDev):
os.unlink(instPath + rootDev)
os.symlink(dmdev, instPath + rootDev)
if not os.path.isdir("%s/etc/lvm" %(instPath,)):
os.makedirs("%s/etc/lvm" %(instPath,))
def filesystemSpace(self, chroot='/'):
space = []
for entry in self.entries:
if not entry.isMounted():
continue
# we can't put swap files on swap partitions; that's nonsense
if entry.mountpoint == "swap":
continue
path = "%s/%s" % (chroot, entry.mountpoint)
try:
space.append((entry.mountpoint, isys.fsSpaceAvailable(path)))
except SystemError:
log("failed to get space available in filesystemSpace() for %s" %(entry.mountpoint,))
def spaceSort(a, b):
(m1, s1) = a
(m2, s2) = b
if (s1 > s2):
return -1
elif s1 < s2:
return 1
return 0
space.sort(spaceSort)
return space
def hasDirtyFilesystems(self, mountpoint):
ret = []
for entry in self.entries:
# XXX - multifsify, virtualize isdirty per fstype
if entry.fsystem.getName() != "ext2": continue
if entry.getFormat(): continue
if isinstance(entry.device.getDevice(), BindMountDevice): continue
try:
if isys.ext2IsDirty(entry.device.getDevice()):
log("%s is a dirty ext2 partition" % entry.device.getDevice())
ret.append(entry.device.getDevice())
except Exception, e:
log("got an exception checking %s for being dirty, hoping it's not" %(entry.device.getDevice(),))
return ret
def umountFilesystems(self, instPath, ignoreErrors = 0):
# XXX remove special case
try:
isys.umount(instPath + '/proc/bus/usb', removeDir = 0)
log("Umount USB OK")
except:
# log("Umount USB Fail")
pass
# take a slice so we don't modify self.entries
reverse = self.entries[:]
reverse.reverse()
for entry in reverse:
entry.umount(instPath)
class FileSystemSetEntry:
def __init__ (self, device, mountpoint,
fsystem=None, options=None,
origfsystem=None, migrate=0,
order=-1, fsck=-1, format=0,
badblocks = 0):
if not fsystem:
fsystem = fileSystemTypeGet("ext2")
self.device = device
self.mountpoint = mountpoint
self.fsystem = fsystem
self.origfsystem = origfsystem
self.migrate = migrate
if options:
self.options = options
else:
self.options = fsystem.getDefaultOptions(mountpoint)
self.mountcount = 0
self.label = None
if fsck == -1:
self.fsck = fsystem.isChecked()
else:
self.fsck = fsck
if order == -1:
if mountpoint == '/':
self.order = 1
elif self.fsck:
self.order = 2
else:
self.order = 0
else:
self.order = order
if format and not fsystem.isFormattable():
raise RuntimeError, ("file system type %s is not formattable, "
"but has been added to fsset with format "
"flag on" % fsystem.getName())
self.format = format
self.badblocks = badblocks
def mount(self, chroot='/', devPrefix='/tmp', readOnly = 0):
device = self.device.setupDevice(chroot, devPrefix=devPrefix)
# FIXME: we really should migrate before turnOnFilesystems.
# but it's too late now
if (self.migrate == 1) and (self.origfsystem is not None):
self.origfsystem.mount(device, "%s/%s" % (chroot, self.mountpoint),
readOnly = readOnly,
bindMount = isinstance(self.device,
BindMountDevice))
else:
self.fsystem.mount(device, "%s/%s" % (chroot, self.mountpoint),
readOnly = readOnly,
bindMount = isinstance(self.device,
BindMountDevice))
self.mountcount = self.mountcount + 1
def umount(self, chroot='/'):
if self.mountcount > 0:
try:
self.fsystem.umount(self.device, "%s/%s" % (chroot,
self.mountpoint))
self.mountcount = self.mountcount - 1
except RuntimeError:
pass
def setFileSystemType(self, fstype):
self.fsystem = fstype
def setBadblocks(self, state):
self.badblocks = state
def getBadblocks(self):
return self.badblocks
def getMountPoint(self):
return self.mountpoint
def setFormat (self, state):
if self.migrate and state:
raise ValueError, "Trying to set format bit on when migrate is set!"
self.format = state
def getFormat (self):
return self.format
def setMigrate (self, state):
if self.format and state:
raise ValueError, "Trying to set migrate bit on when format is set!"
self.migrate = state
def getMigrate (self):
return self.migrate
def isMounted (self):
return self.mountcount > 0
def getLabel (self):
return self.label
def setLabel (self, label):
self.label = label
def __str__(self):
if not self.mountpoint:
mntpt = "None"
else:
mntpt = self.mountpoint
str = ("fsentry -- device: %(device)s mountpoint: %(mountpoint)s\n"
" fsystem: %(fsystem)s format: %(format)s\n"
" ismounted: %(mounted)s \n"%
{"device": self.device.getDevice(), "mountpoint": mntpt,
"fsystem": self.fsystem.getName(), "format": self.format,
"mounted": self.mountcount})
return str
class Device:
def __init__(self):
self.device = "none"
self.fsoptions = {}
self.label = None
self.isSetup = 0
self.doLabel = 1
def getComment (self):
return ""
def getDevice (self, asBoot = 0):
return self.device
def setupDevice (self, chroot='/', devPrefix='/tmp'):
return self.device
def cleanupDevice (self, chroot, devPrefix='/tmp'):
pass
def solidify (self):
pass
def getName(self):
return self.__class__.__name__
def getLabel(self):
try:
return isys.readFSLabel(self.setupDevice(), makeDevNode = 0)
except:
return ""
class DevDevice(Device):
"""Device with a device node rooted in /dev that we just always use
the pre-created device node for."""
def __init__(self, dev):
Device.__init__(self)
self.device = dev
def getDevice(self, asBoot = 0):
return self.device
def setupDevice(self, chroot='/', devPrefix='/dev'):
return "/dev/%s" %(self.getDevice(),)
class RAIDDevice(Device):
# XXX usedMajors does not take in account any EXISTING md device
# on the system for installs. We need to examine all partitions
# to investigate which minors are really available.
usedMajors = {}
# members is a list of Device based instances that will be
# a part of this raid device
def __init__(self, level, members, minor=-1, spares=0, existing=0,
chunksize = 64):
Device.__init__(self)
self.level = level
self.members = members
self.spares = spares
self.numDisks = len(members) - spares
self.isSetup = existing
self.doLabel = None
if chunksize is not None:
self.chunksize = chunksize
else:
self.chunksize = 256
if len(members) < spares:
raise RuntimeError, ("you requiested more spare devices "
"than online devices!")
if level == 5:
if self.numDisks < 3:
raise RuntimeError, "RAID 5 requires at least 3 online members"
# there are 32 major md devices, 0...31
if minor == -1 or minor is None:
for I in range(32):
if not RAIDDevice.usedMajors.has_key(I):
minor = I
break
if minor == -1:
raise RuntimeError, ("Unable to allocate minor number for "
"raid device")
RAIDDevice.usedMajors[minor] = None
self.device = "md" + str(minor)
self.minor = minor
# make sure the list of raid members is sorted
self.members.sort()
def __del__ (self):
del RAIDDevice.usedMajors[self.minor]
def ext2Args (self):
if self.level == 5:
return [ '-R', 'stride=%d' % ((self.numDisks - 1) * 16) ]
elif self.level == 0:
return [ '-R', 'stride=%d' % (self.numDisks * 16) ]
return []
def mdadmLine (self, devPrefix="/dev"):
return "ARRAY %s/%s\n" %(devPrefix, self.device)
def raidTab (self, devPrefix='/dev'):
if self.level == 1:
nDisks = max(2, self.numDisks)
elif self.level == 5:
nDisks = max(3, self.numDisks)
elif self.level == 6:
nDisks = max(4, self.numDisks)
else:
nDisks = self.numDisks
entry = ""
entry = entry + "raiddev %s/%s\n" % (devPrefix,
self.device,)
entry = entry + "raid-level %d\n" % (self.level,)
entry = entry + "nr-raid-disks %d\n" % (nDisks,)
entry = entry + "chunk-size %s\n" %(self.chunksize,)
entry = entry + "persistent-superblock 1\n"
entry = entry + "nr-spare-disks %d\n" % (self.spares,)
i = 0
for device in self.members[:self.numDisks]:
entry = entry + " device %s/%s\n" % (devPrefix,
device)
entry = entry + " raid-disk %d\n" % (i,)
i = i + 1
while i < nDisks:
entry = entry + " device dev/null\n"
entry = entry + " failed-disk %d\n" % (i,)
i = i + 1
i = 0
for device in self.members[self.numDisks:]:
entry = entry + " device %s/%s\n" % (devPrefix,
device)
entry = entry + " spare-disk %d\n" % (i,)
i = i + 1
return entry
def setupDevice (self, chroot="/", devPrefix='/dev'):
def devify(x):
return "/dev/%s" %(x,)
if self.level == 1:
nDisks = max(2, self.numDisks)
elif self.level == 5:
nDisks = max(3, self.numDisks)
elif self.level == 6:
nDisks = max(4, self.numDisks)
else:
nDisks = self.numDisks
node = "%s/%s" % (devPrefix, self.device)
isys.makeDevInode(self.device, node)
if not self.isSetup:
for device in self.members:
PartitionDevice(device).setupDevice(chroot,
devPrefix=devPrefix)
args = ["/usr/sbin/mdadm", "--create", "/dev/%s" %(self.device,),
"--run", "--chunk=%s" %(self.chunksize,),
"--level=%s" %(self.level,),
"--raid-devices=%s" %(nDisks,)]
if self.spares > 0:
args.append("--spare-devices=%s" %(self.spares,),)
args.extend(map(devify, self.members))
i = 0
while self.numDisks + i < nDisks:
args.append("missing")
i = i + 1
log("going to run: %s" %(args,))
iutil.execWithRedirect (args[0], args,
stderr="/dev/tty5", stdout="/dev/tty5")
raid.register_raid_device(self.device, self.members[:],
self.level, self.numDisks)
self.isSetup = 1
else:
isys.raidstart(self.device, self.members[0])
return node
def getDevice (self, asBoot = 0):
if not asBoot:
return self.device
else:
return self.members[0]
def solidify(self):
return
ext2 = fileSystemTypeGet("ext2")
ext2.registerDeviceArgumentFunction(RAIDDevice, RAIDDevice.ext2Args)
class VolumeGroupDevice(Device):
def __init__(self, name, physvols, pesize = 32768, existing = 0):
"""Creates a VolumeGroupDevice.
name is the name of the volume group
physvols is a list of Device objects which are the physical volumes
pesize is the size of physical extents in kilobytes
existing is whether this vg previously existed.
"""
Device.__init__(self)
self.physicalVolumes = physvols
self.isSetup = existing
self.name = name
self.device = name
self.isSetup = existing
self.physicalextentsize = pesize
def setupDevice (self, chroot="/", devPrefix='/tmp'):
nodes = []
for volume in self.physicalVolumes:
# XXX the lvm tools are broken and will only work for /dev
node = volume.setupDevice(chroot, devPrefix="/dev")
# XXX I should check if the pv is set up somehow so that we
# can have preexisting vgs and add new pvs to them.
if not self.isSetup:
# now make the device into a real physical volume
# XXX I don't really belong here. should
# there be a PhysicalVolumeDevice(PartitionDevice) ?
lvm.writeForceConf()
rc = iutil.execWithRedirect("lvm",
["lvm", "pvcreate", "-ff", "-y",
"-v", node],
stdout = "/tmp/lvmout",
stderr = "/tmp/lvmout",
searchPath = 1)
if rc:
raise SystemError, "pvcreate failed for %s" % (volume,)
lvm.unlinkConf()
lvm.wipeOtherMetadataFromPV(node)
nodes.append(node)
if not self.isSetup:
# rescan now that we've recreated pvs. ugh.
lvm.writeForceConf()
lvm.vgscan()
args = [ "lvm", "vgcreate", "-v", "-An",
"-s", "%sk" %(self.physicalextentsize,),
self.name ]
args.extend(nodes)
rc = iutil.execWithRedirect(args[0], args,
stdout = "/tmp/lvmout",
stderr = "/tmp/lvmout",
searchPath = 1)
if rc:
raise SystemError, "vgcreate failed for %s" %(self.name,)
lvm.unlinkConf()
self.isSetup = 1
else:
lvm.vgscan()
lvm.vgactivate()
return "/dev/%s" % (self.name,)
def solidify(self):
return
class LogicalVolumeDevice(Device):
# note that size is in megabytes!
def __init__(self, volumegroup, size, vgname, existing = 0):
Device.__init__(self)
self.volumeGroup = volumegroup
self.size = size
self.name = vgname
self.isSetup = 0
self.isSetup = existing
self.doLabel = None
# these are attributes we might want to expose. or maybe not.
# self.chunksize
# self.stripes
# self.stripesize
# self.extents
# self.readaheadsectors
def setupDevice(self, chroot="/", devPrefix='/tmp'):
if not self.isSetup:
lvm.writeForceConf()
rc = iutil.execWithRedirect("lvm",
["lvm", "lvcreate", "-L",
"%dM" % (self.size,),
"-n", self.name, "-An",
self.volumeGroup],
stdout = "/tmp/lvmout",
stderr = "/tmp/lvmout",
searchPath = 1)
if rc:
raise SystemError, "lvcreate failed for %s" %(self.name,)
lvm.unlinkConf()
self.isSetup = 1
return "/dev/%s" % (self.getDevice(),)
def getDevice(self, asBoot = 0):
return "%s/%s" % (self.volumeGroup, self.name)
def solidify(self):
return
class PartitionDevice(Device):
def __init__(self, partition):
Device.__init__(self)
if type(partition) != types.StringType:
raise ValueError, "partition must be a string"
self.device = partition
def setupDevice(self, chroot="/", devPrefix='/tmp'):
path = '%s/%s' % (devPrefix, self.getDevice(),)
isys.makeDevInode(self.getDevice(), path)
return path
class PartedPartitionDevice(PartitionDevice):
def __init__(self, partition):
Device.__init__(self)
self.device = None
self.partition = partition
def getDevice(self, asBoot = 0):
if not self.partition:
return self.device
return partedUtils.get_partition_name(self.partition)
def solidify(self):
# drop reference on the parted partition object and note
# the current minor number allocation
self.device = self.getDevice()
self.partition = None
class BindMountDevice(Device):
def __init__(self, directory):
Device.__init__(self)
self.device = directory
def setupDevice(self, chroot="/", devPrefix="/tmp"):
return chroot + self.device
class SwapFileDevice(Device):
def __init__(self, file):
Device.__init__(self)
self.device = file
self.size = 0
def setSize (self, size):
self.size = size
def setupDevice (self, chroot="/", devPrefix='/tmp'):
file = os.path.normpath(chroot + self.getDevice())
if not os.access(file, os.R_OK):
if self.size:
# make sure the permissions are set properly
fd = os.open(file, os.O_CREAT, 0600)
os.close(fd)
isys.ddfile(file, self.size, None)
else:
raise SystemError, (0, "swap file creation necessary, but "
"required size is unknown.")
return file
# This is a device that describes a swap file that is sitting on
# the loopback filesystem host for partitionless installs.
# The piggypath is the place where the loopback file host filesystem
# will be mounted
class PiggybackSwapFileDevice(SwapFileDevice):
def __init__(self, piggypath, file):
SwapFileDevice.__init__(self, file)
self.piggypath = piggypath
def setupDevice(self, chroot="/", devPrefix='/tmp'):
return SwapFileDevice.setupDevice(self, self.piggypath, devPrefix)
class LoopbackDevice(Device):
def __init__(self, hostPartition, hostFs):
Device.__init__(self)
self.host = "/dev/" + hostPartition
self.hostfs = hostFs
self.device = "loop1"
def setupDevice(self, chroot="/", devPrefix='/tmp/'):
if not self.isSetup:
isys.mount(self.host[5:], "/mnt/loophost", fstype = "vfat")
self.device = allocateLoopback("/mnt/loophost/redhat.img")
if not self.device:
raise SystemError, "Unable to allocate loopback device"
self.isSetup = 1
path = '%s/%s' % (devPrefix, self.getDevice())
else:
path = '%s/%s' % (devPrefix, self.getDevice())
isys.makeDevInode(self.getDevice(), path)
path = os.path.normpath(path)
return path
def getComment (self):
return "# LOOP1: %s %s /redhat.img\n" % (self.host, self.hostfs)
def makeDevice(dev):
if dev.startswith('md'):
try:
(mdname, devices, level, numActive) = raid.lookup_raid_device(dev)
device = RAIDDevice(level, devices,
minor=int(mdname[2:]),
spares=len(devices) - numActive,
existing=1)
except KeyError:
device = DevDevice(dev)
else:
device = DevDevice(dev)
return device
# XXX fix RAID
def readFstab (path, intf = None):
fsset = FileSystemSet()
# first, we look at all the disks on the systems and get any ext2/3
# labels off of the filesystem.
# temporary, to get the labels
diskset = partedUtils.DiskSet()
diskset.openDevices()
labels = diskset.getLabels()
labelToDevice = {}
for device, label in labels.items():
if not labelToDevice.has_key(label):
labelToDevice[label] = device
elif intf is not None:
intf.messageWindow(_("Duplicate Labels"),
_("Multiple devices on your system are "
"labelled %s. Labels across devices must be "
"unique for your system to function "
"properly.\n\n"
"Please fix this problem and restart the "
"installation process.") %(label,),
type="custom", custom_icon="error",
custom_buttons=[_("_Reboot")])
sys.exit(0)
else:
log("WARNING!!! Duplicate labels for %s, but no intf so trying "
"to continue" %(label,))
# mark these labels found on the system as used so the factory
# doesn't give them to another device
labelFactory.reserveLabels(labels)
loopIndex = {}
f = open (path, "r")
lines = f.readlines ()
f.close()
for line in lines:
fields = string.split (line)
if not fields: continue
if line[0] == "#":
# skip all comments
continue
# all valid fstab entries have 6 fields; if the last two are missing
# they are assumed to be zero per fstab(5)
if len(fields) < 4:
continue
elif len(fields) == 4:
fields.append(0)
fields.append(0)
elif len(fields) == 5:
fields.append(0)
elif len(fields) > 6:
continue
if string.find(fields[3], "noauto") != -1: continue
# shenanigans to handle ext3,ext2 format in fstab
fstotry = fields[2]
if fstotry.find(","):
fstotry = fstotry.split(",")
else:
fstotry = [ fstotry ]
fsystem = None
for fs in fstotry:
# if we don't support mounting the filesystem, continue
if not fileSystemTypes.has_key(fs):
continue
fsystem = fileSystemTypeGet(fs)
break
if fsystem is None:
continue
label = None
if fields[0] == "none":
device = Device()
elif ((string.find(fields[3], "bind") != -1) and
fields[0].startswith("/")):
# it's a bind mount, they're Weird (tm)
device = BindMountDevice(fields[0])
fsystem = fileSystemTypeGet("bind")
elif len(fields) >= 6 and fields[0].startswith('LABEL='):
label = fields[0][6:]
if labelToDevice.has_key(label):
device = makeDevice(labelToDevice[label])
else:
log ("Warning: fstab file has LABEL=%s, but this label "
"could not be found on any file system", label)
# bad luck, skip this entry.
continue
elif fields[2] == "swap" and not fields[0].startswith('/dev/'):
# swap files
file = fields[0]
if file.startswith('/initrd/loopfs/'):
file = file[14:]
device = PiggybackSwapFileDevice("/mnt/loophost", file)
else:
device = SwapFileDevice(file)
elif fields[0].startswith('/dev/loop'):
# look up this loop device in the index to find the
# partition that houses the filesystem image
# XXX currently we assume /dev/loop1
if loopIndex.has_key(device):
(dev, fs) = loopIndex[device]
device = LoopbackDevice(dev, fs)
elif fields[0].startswith('/dev/'):
device = makeDevice(fields[0][5:])
else:
continue
# if they have a filesystem being mounted as auto, we need
# to sniff around a bit to figure out what it might be
# if we fail at all, though, just ignore it
if fsystem == "auto" and device.getDevice() != "none":
try:
tmp = partedUtils.sniffFilesystemType("/dev/%s" %(device.setupDevice(),))
if tmp is not None:
fsystem = tmp
except:
pass
entry = FileSystemSetEntry(device, fields[1], fsystem, fields[3],
origfsystem=fsystem)
if label:
entry.setLabel(label)
fsset.add(entry)
return fsset
def getDevFD(device):
try:
fd = os.open(device, os.O_RDONLY)
except:
file = '/tmp/' + device
try:
isys.makeDevInode(device, file)
fd = os.open(file, os.O_RDONLY)
except:
return -1
return fd
def isValidExt2(device):
fd = getDevFD(device)
if fd == -1:
return 0
buf = os.read(fd, 2048)
os.close(fd)
if len(buf) != 2048:
return 0
if struct.unpack("<H", buf[1080:1082]) == (0xef53,):
return 1
return 0
def isValidXFS(device):
fd = getDevFD(device)
if fd == -1:
return 0
buf = os.read(fd, 4)
os.close(fd)
if len(buf) != 4:
return 0
if buf == "XFSB":
return 1
return 0
def isValidReiserFS(device):
fd = getDevFD(device)
if fd == -1:
return 0
'''
** reiserfs 3.5.x super block begins at offset 8K
** reiserfs 3.6.x super block begins at offset 64K
All versions have a magic value of "ReIsEr" at
offset 0x34 from start of super block
'''
reiserMagicVal = "ReIsEr"
reiserMagicOffset = 0x34
reiserSBStart = [64*1024, 8*1024]
bufSize = 0x40 # just large enough to include the magic value
for SBOffset in reiserSBStart:
try:
os.lseek(fd, SBOffset, 0)
buf = os.read(fd, bufSize)
except:
buf = ""
if len(buf) < bufSize:
continue
if (buf[reiserMagicOffset:reiserMagicOffset+len(reiserMagicVal)] ==
reiserMagicVal):
os.close(fd)
return 1
os.close(fd)
return 0
def isValidJFS(device):
fd = getDevFD(device)
if fd == -1:
return 0
try:
os.lseek(fd, 32768, 0)
buf = os.read(fd, 128)
except:
buf = ""
os.close(fd)
if len(buf) < 4:
return 0
if (buf[0:4] == "JFS1"):
return 1
return 0
# this will return a list of types of filesystems which device
# looks like it could be to try mounting as
def getFStoTry(device):
rc = []
if isValidXFS(device):
rc.append("xfs")
if isValidReiserFS(device):
rc.append("reiserfs")
if isValidJFS(device):
rc.append("jfs")
if isValidExt2(device):
if os.access(device, os.O_RDONLY):
create = 0
else:
create = 1
if isys.ext2HasJournal(device, makeDevNode = create):
rc.append("ext3")
rc.append("ext2")
# FIXME: need to check for swap
return rc
def allocateLoopback(file):
found = 1
for i in range(8):
dev = "loop%d" % (i,)
path = "/tmp/loop%d" % (i,)
isys.makeDevInode(dev, path)
try:
isys.losetup(path, file)
found = 1
except SystemError:
continue
break
if found:
return dev
return None
def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint):
if windowCreator:
w = windowCreator(_("Formatting"),
_("Formatting %s file system...") % (mntpoint,), 100)
else:
w = None
fd = os.open(messageFile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
p = os.pipe()
childpid = os.fork()
if not childpid:
os.close(p[0])
os.dup2(p[1], 1)
os.dup2(fd, 2)
os.close(p[1])
os.close(fd)
os.execv(argList[0], argList)
log("failed to exec %s", argList)
os._exit(1)
os.close(p[1])
# ignoring SIGCHLD would be cleaner then ignoring EINTR, but
# we can't use signal() in this thread?
s = 'a'
while s and s != '\b':
try:
s = os.read(p[0], 1)
except OSError, args:
(num, str) = args
if (num != 4):
raise IOError, args
os.write(fd, s)
num = ''
sync = 0
while s:
try:
s = os.read(p[0], 1)
os.write(fd, s)
if s != '\b':
try:
num = num + s
except:
pass
else:
if num and len(num):
l = string.split(num, '/')
try:
val = (int(l[0]) * 100) / int(l[1])
except (IndexError, TypeError):
pass
else:
w and w.set(val)
# sync every 10%
if sync + 10 < val:
isys.sync()
sync = val
num = ''
except OSError, args:
(errno, str) = args
if (errno != 4):
raise IOError, args
try:
(pid, status) = os.waitpid(childpid, 0)
except OSError, (num, msg):
log("exception from waitpid while formatting: %s %s" %(num, msg))
status = None
os.close(fd)
w and w.pop()
# *shrug* no clue why this would happen, but hope that things are fine
if status is None:
return 0
if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0):
return 0
return 1
if __name__ == "__main__":
log.open("foo")
fsset = readFstab("fstab")
print fsset.fstab()
sys.exit(0)
fsset = FileSystemSet()
proc = FileSystemSetEntry(Device(), '/proc', 'proc')
fsset.add(proc)
devpts = FileSystemSetEntry(Device(), '/dev/pts', 'devpts')
fsset.add(devpts)
device = LoopbackDevice("hda1", "vfat")
mountpoint = FileSystemSetEntry (device, '/')
fsset.add(mountpoint)
device = SwapFileDevice("/SWAP")
mountpoint = FileSystemSetEntry (device, "swap", "swap")
fsset.add(mountpoint)
print fsset.fstab()