]>
Commit | Line | Data |
---|---|---|
99237823 MT |
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 | |
6a9a6ae9 | 27 | import time |
99237823 MT |
28 | import partitioning |
29 | import partedUtils | |
30 | import types | |
69395171 | 31 | import math |
99237823 MT |
32 | from flags import flags |
33 | ||
62c1bebc MT |
34 | import gettext |
35 | _ = lambda x: gettext.ldgettext("pomona", x) | |
36 | N_ = lambda x: x | |
99237823 MT |
37 | |
38 | import logging | |
39 | log = logging.getLogger("pomona") | |
40 | ||
41 | class BadBlocksError(Exception): | |
082638e9 | 42 | pass |
99237823 MT |
43 | |
44 | class SuspendError(Exception): | |
082638e9 | 45 | pass |
99237823 MT |
46 | |
47 | class OldSwapError(Exception): | |
082638e9 | 48 | pass |
99237823 | 49 | |
69395171 MT |
50 | class ResizeError(Exception): |
51 | pass | |
52 | ||
99237823 MT |
53 | defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr', '/var', '/opt'] |
54 | ||
55 | fileSystemTypes = {} | |
56 | ||
57 | def fileSystemTypeGetDefault(): | |
f6099fef MT |
58 | if fileSystemTypeGet('ext4').isSupported(): |
59 | return fileSystemTypeGet('ext4') | |
60 | elif fileSystemTypeGet('ext3').isSupported(): | |
082638e9 MT |
61 | return fileSystemTypeGet('ext3') |
62 | elif fileSystemTypeGet('ext2').isSupported(): | |
63 | return fileSystemTypeGet('ext2') | |
64 | else: | |
f6099fef | 65 | raise ValueError, "You have neither ext4, ext3 or ext2 support in your kernel!" |
99237823 MT |
66 | |
67 | def fileSystemTypeGet(key): | |
082638e9 | 68 | return fileSystemTypes[key] |
99237823 MT |
69 | |
70 | def fileSystemTypeRegister(klass): | |
082638e9 | 71 | fileSystemTypes[klass.getName()] = klass |
99237823 MT |
72 | |
73 | def fileSystemTypeGetTypes(): | |
082638e9 | 74 | return fileSystemTypes.copy() |
99237823 MT |
75 | |
76 | def getUsableLinuxFs(): | |
082638e9 MT |
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 | |
99237823 MT |
90 | |
91 | def devify(device): | |
082638e9 MT |
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 | |
99237823 MT |
102 | |
103 | class LabelFactory: | |
082638e9 MT |
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 | |
99237823 MT |
144 | |
145 | labelFactory = LabelFactory() | |
146 | ||
147 | class FileSystemType: | |
082638e9 | 148 | kernelFilesystems = {} |
d593e226 MT |
149 | lostAndFoundContext = None |
150 | ||
082638e9 MT |
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 | |
d593e226 MT |
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 | |
082638e9 MT |
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)) | |
6a9a6ae9 MT |
202 | log.debug("mounting %s on %s/%s as %s" %(device, instroot, |
203 | mountpoint, self.getMountName())) | |
082638e9 | 204 | isys.mount(device, "%s/%s" %(instroot, mountpoint), |
6a9a6ae9 MT |
205 | fstype = self.getMountName(), |
206 | readOnly = readOnly, bindMount = bindMount, | |
207 | options = self.defaultOptions) | |
082638e9 MT |
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 | ||
6a9a6ae9 MT |
220 | def getMountName(self, quoted = 0): |
221 | return self.getName(quoted) | |
222 | ||
082638e9 MT |
223 | def registerDeviceArgumentFunction(self, klass, function): |
224 | self.deviceArguments[klass] = function | |
225 | ||
082638e9 MT |
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 | ||
d593e226 MT |
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 | ||
082638e9 MT |
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 | ||
d593e226 | 279 | return FileSystemType.kernelFilesystems.has_key(self.getMountName()) or self.getName() == "auto" |
082638e9 MT |
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 | |
99237823 MT |
324 | |
325 | ||
326 | class reiserfsFileSystem(FileSystemType): | |
082638e9 MT |
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" | |
082638e9 MT |
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", | |
d8ea2283 MT |
345 | [devicePath], |
346 | stdin = p[0], | |
347 | stdout = "/dev/tty5", | |
348 | stderr = "/dev/tty5", searchPath = 1) | |
082638e9 MT |
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", | |
d8ea2283 MT |
357 | ["--label", label, devicePath], |
358 | stdout = "/dev/tty5", | |
359 | stderr = "/dev/tty5", searchPath = 1) | |
082638e9 MT |
360 | if rc: |
361 | raise SystemError | |
362 | entry.setLabel(label) | |
363 | ||
99237823 MT |
364 | fileSystemTypeRegister(reiserfsFileSystem()) |
365 | ||
366 | class xfsFileSystem(FileSystemType): | |
082638e9 MT |
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 | |
082638e9 MT |
377 | |
378 | def formatDevice(self, entry, progress, chroot='/'): | |
379 | devicePath = entry.device.setupDevice(chroot) | |
380 | ||
381 | rc = inutil.execWithRedirect("mkfs.xfs", | |
d8ea2283 MT |
382 | ["-f", "-l", "internal", |
383 | "-i", "attr=2", devicePath], | |
384 | stdout = "/dev/tty5", | |
385 | stderr = "/dev/tty5", searchPath = 1) | |
082638e9 MT |
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", | |
d8ea2283 MT |
395 | ["-x", "-c", db_cmd, devicePath], |
396 | stdout = "/dev/tty5", | |
397 | stderr = "/dev/tty5", searchPath = 1) | |
082638e9 MT |
398 | if rc: |
399 | raise SystemError | |
400 | entry.setLabel(label) | |
401 | ||
99237823 MT |
402 | fileSystemTypeRegister(xfsFileSystem()) |
403 | ||
404 | class extFileSystem(FileSystemType): | |
082638e9 MT |
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 | |
73fb39f7 MT |
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 | ||
ec150b24 | 427 | rc = inutil.execWithPulseProgress("e2fsck", ["-f", "-p", "-C", "0", devicePath], |
73fb39f7 MT |
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,)) | |
ec150b24 | 440 | rc = inutil.execWithPulseProgress("resize2fs", |
73fb39f7 MT |
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 | |
ec150b24 | 455 | buf = inutil.execWithCapture("dumpe2fs", |
73fb39f7 MT |
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 | |
082638e9 MT |
490 | |
491 | def labelDevice(self, entry, chroot): | |
492 | devicePath = entry.device.setupDevice(chroot) | |
73fb39f7 MT |
493 | label = self.createLabel(entry.mountpoint, self.maxLabelChars, |
494 | kslabel = entry.label) | |
082638e9 | 495 | |
ec150b24 | 496 | rc = inutil.execWithRedirect("e2label", |
73fb39f7 MT |
497 | [devicePath, label], |
498 | stdout = "/dev/tty5", | |
499 | stderr = "/dev/tty5", searchPath = 1) | |
082638e9 MT |
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) | |
73fb39f7 | 507 | args = [ "mke2fs", devicePath ] |
082638e9 | 508 | |
73fb39f7 MT |
509 | fsProfileArgs = self.getFsProfileArgs() |
510 | if fsProfileArgs: | |
511 | args.extend(fsProfileArgs) | |
082638e9 MT |
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", | |
d8ea2283 MT |
518 | progress, |
519 | entry.mountpoint) | |
082638e9 MT |
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 | |
73fb39f7 | 528 | # of the ext2 fstype, so it needs to be here. FIXME should be moved |
082638e9 MT |
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 | ||
ec150b24 | 536 | rc = inutil.execWithRedirect("tune2fs", |
73fb39f7 MT |
537 | ["-c0", "-i0", |
538 | "-ouser_xattr,acl", devicePath], | |
539 | stdout = "/dev/tty5", | |
540 | stderr = "/dev/tty5", searchPath = 1) | |
99237823 MT |
541 | |
542 | class ext2FileSystem(extFileSystem): | |
082638e9 MT |
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 " | |
d8ea2283 | 554 | "origfsystem set") |
082638e9 | 555 | if entry.fsystem.getName() != "ext3": |
73fb39f7 MT |
556 | raise RuntimeError, ("Trying to migrate ext2 to something other " |
557 | "than ext3") | |
082638e9 MT |
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 | ||
ec150b24 | 564 | rc = inutil.execWithRedirect("tune2fs", |
73fb39f7 MT |
565 | ["-j", devicePath ], |
566 | stdout = "/dev/tty5", | |
567 | stderr = "/dev/tty5", searchPath = 1) | |
082638e9 MT |
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 " | |
d8ea2283 | 577 | "running tune2fs.\n" % (devicePath)) |
082638e9 MT |
578 | if message: |
579 | rc = message(_("Error"), | |
73fb39f7 MT |
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?") | |
d8ea2283 | 584 | % (devicePath, devicePath), type = "yesno") |
082638e9 MT |
585 | if rc == 0: |
586 | sys.exit(0) | |
73fb39f7 | 587 | entry.fsystem = entry.origfsystem |
84b042fd MT |
588 | else: |
589 | extFileSystem.setExt3Options(self, entry, message, chroot) | |
99237823 | 590 | |
73fb39f7 | 591 | |
99237823 MT |
592 | fileSystemTypeRegister(ext2FileSystem()) |
593 | ||
594 | class ext3FileSystem(extFileSystem): | |
082638e9 MT |
595 | def __init__(self): |
596 | extFileSystem.__init__(self) | |
597 | self.name = "ext3" | |
73fb39f7 | 598 | self.extraFormatArgs = [ "-t", "ext3" ] |
082638e9 | 599 | self.partedFileSystemType = parted.file_system_type_get("ext3") |
73fb39f7 | 600 | self.migratetofs = ['ext4dev'] |
99237823 | 601 | |
082638e9 MT |
602 | def formatDevice(self, entry, progress, chroot='/'): |
603 | extFileSystem.formatDevice(self, entry, progress, chroot) | |
604 | extFileSystem.setExt3Options(self, entry, progress, chroot) | |
99237823 | 605 | |
73fb39f7 MT |
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" | |
ec150b24 | 617 | rc = inutil.execWithRedirect("tune2fs", |
73fb39f7 MT |
618 | ["-E", "test_fs", devicePath ], |
619 | stdout = "/dev/tty5", | |
620 | stderr = "/dev/tty5", searchPath = 1) | |
621 | if rc: | |
622 | raise SystemError | |
623 | ||
99237823 MT |
624 | fileSystemTypeRegister(ext3FileSystem()) |
625 | ||
73fb39f7 MT |
626 | class ext4FileSystem(extFileSystem): |
627 | def __init__(self): | |
628 | extFileSystem.__init__(self) | |
f6099fef | 629 | self.name = "ext4" |
73fb39f7 | 630 | self.partedFileSystemType = parted.file_system_type_get("ext3") |
f6099fef | 631 | self.extraFormatArgs = [ "-t", "ext4" ] |
73fb39f7 MT |
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 | ||
99237823 | 640 | class swapFileSystem(FileSystemType): |
082638e9 MT |
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", | |
d8ea2283 MT |
687 | ["-v1", file], |
688 | stdout = "/dev/tty5", | |
689 | stderr = "/dev/tty5", | |
690 | searchPath = 1) | |
082638e9 MT |
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", | |
d8ea2283 MT |
707 | ["-v1", "-L", label, file], |
708 | stdout = "/dev/tty5", | |
709 | stderr = "/dev/tty5", | |
710 | searchPath = 1) | |
082638e9 MT |
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 | |
99237823 MT |
729 | |
730 | fileSystemTypeRegister(swapFileSystem()) | |
731 | ||
732 | class FATFileSystem(FileSystemType): | |
082638e9 MT |
733 | def __init__(self): |
734 | FileSystemType.__init__(self) | |
735 | self.partedFileSystemType = parted.file_system_type_get("fat32") | |
84b042fd | 736 | self.formattable = 0 |
082638e9 MT |
737 | self.checked = 0 |
738 | self.maxSizeMB = 1024 * 1024 | |
739 | self.name = "vfat" | |
082638e9 | 740 | |
99237823 MT |
741 | fileSystemTypeRegister(FATFileSystem()) |
742 | ||
743 | class NTFSFileSystem(FileSystemType): | |
082638e9 MT |
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" | |
99237823 MT |
750 | |
751 | fileSystemTypeRegister(NTFSFileSystem()) | |
752 | ||
753 | class ForeignFileSystem(FileSystemType): | |
082638e9 MT |
754 | def __init__(self): |
755 | FileSystemType.__init__(self) | |
756 | self.formattable = 0 | |
757 | self.checked = 0 | |
758 | self.name = "foreign" | |
99237823 | 759 | |
082638e9 MT |
760 | def formatDevice(self, entry, progress, chroot='/'): |
761 | return | |
99237823 MT |
762 | |
763 | fileSystemTypeRegister(ForeignFileSystem()) | |
764 | ||
765 | class PsudoFileSystem(FileSystemType): | |
082638e9 MT |
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 | |
99237823 | 772 | |
082638e9 MT |
773 | def isKernelFS(self): |
774 | return True | |
99237823 MT |
775 | |
776 | class ProcFileSystem(PsudoFileSystem): | |
082638e9 MT |
777 | def __init__(self): |
778 | PsudoFileSystem.__init__(self, "proc") | |
99237823 MT |
779 | |
780 | fileSystemTypeRegister(ProcFileSystem()) | |
781 | ||
782 | class SysfsFileSystem(PsudoFileSystem): | |
082638e9 MT |
783 | def __init__(self): |
784 | PsudoFileSystem.__init__(self, "sysfs") | |
99237823 MT |
785 | |
786 | fileSystemTypeRegister(SysfsFileSystem()) | |
787 | ||
788 | class DevptsFileSystem(PsudoFileSystem): | |
082638e9 MT |
789 | def __init__(self): |
790 | PsudoFileSystem.__init__(self, "devpts") | |
791 | self.defaultOptions = "gid=5,mode=620" | |
99237823 | 792 | |
082638e9 MT |
793 | def isMountable(self): |
794 | return 0 | |
99237823 MT |
795 | |
796 | fileSystemTypeRegister(DevptsFileSystem()) | |
797 | ||
798 | class DevshmFileSystem(PsudoFileSystem): | |
082638e9 MT |
799 | def __init__(self): |
800 | PsudoFileSystem.__init__(self, "tmpfs") | |
801 | ||
802 | def isMountable(self): | |
803 | return 0 | |
99237823 | 804 | |
99237823 MT |
805 | fileSystemTypeRegister(DevshmFileSystem()) |
806 | ||
807 | class AutoFileSystem(PsudoFileSystem): | |
082638e9 MT |
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)) | |
082638e9 MT |
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) | |
99237823 MT |
832 | |
833 | fileSystemTypeRegister(AutoFileSystem()) | |
834 | ||
835 | class BindFileSystem(PsudoFileSystem): | |
082638e9 MT |
836 | def __init__(self): |
837 | PsudoFileSystem.__init__(self, "bind") | |
838 | ||
839 | def isMountable(self): | |
840 | return 1 | |
99237823 | 841 | |
99237823 MT |
842 | fileSystemTypeRegister(BindFileSystem()) |
843 | ||
844 | class FileSystemSet: | |
082638e9 MT |
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" | |
d8ea2283 MT |
915 | # "adding entry for %s\n" |
916 | # "entry object %s, class __dict__ is %s", | |
917 | # self, entry.mountpoint, entry, | |
918 | # isys.printObject(entry.__dict__)) | |
082638e9 MT |
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 | ||
6a9a6ae9 | 959 | def fstab (self): |
082638e9 | 960 | format = "%-23s %-23s %-7s %-15s %d %d\n" |
6a9a6ae9 MT |
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 | ||
082638e9 MT |
971 | for entry in self.entries: |
972 | if entry.mountpoint: | |
6a9a6ae9 MT |
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: | |
082638e9 MT |
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, | |
6a9a6ae9 MT |
981 | entry.fsystem.getMountName(), |
982 | entry.getOptions(), entry.fsck, | |
d8ea2283 | 983 | entry.order) |
082638e9 MT |
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()), | |
d8ea2283 MT |
1001 | entry.mountpoint, |
1002 | entry.fsystem.getName(), | |
1003 | options) | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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(),)) | |
082638e9 MT |
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" | |
d8ea2283 MT |
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()) | |
082638e9 MT |
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" | |
d8ea2283 MT |
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()) | |
082638e9 MT |
1182 | else: |
1183 | msg = _("The swap device:\n\n /dev/%s\n\n" | |
d8ea2283 MT |
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()) | |
082638e9 MT |
1191 | |
1192 | # choose your own adventure swap partitions... | |
1193 | msg = msg + _("\n\nChoose Skip if you want the " | |
d8ea2283 MT |
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.") | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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)) | |
082638e9 MT |
1216 | else: |
1217 | self.messageWindow(_("Error"), | |
d8ea2283 MT |
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)) | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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(),)) | |
082638e9 MT |
1278 | sys.exit(0) |
1279 | ||
1280 | except SystemError: | |
1281 | if self.messageWindow: | |
1282 | self.messageWindow(_("Error"), | |
d8ea2283 MT |
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(),)) | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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(),)) | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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(),)) | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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,)) | |
082638e9 MT |
1389 | else: |
1390 | self.messageWindow(_("Invalid mount point"), | |
d8ea2283 MT |
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)) | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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")]) | |
082638e9 MT |
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: " | |
d8ea2283 MT |
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) | |
082638e9 MT |
1426 | else: |
1427 | errStr = _("Error mounting device %s as %s: " | |
d8ea2283 MT |
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) | |
082638e9 MT |
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: | |
69395171 | 1448 | space.append((entry.mountpoint, isys.pathSpaceAvailable(path))) |
082638e9 MT |
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: | |
d8ea2283 | 1489 | # log.error("Umount USB Fail") |
082638e9 MT |
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) | |
99237823 MT |
1500 | |
1501 | class FileSystemSetEntry: | |
082638e9 | 1502 | def __init__ (self, device, mountpoint, |
d8ea2283 MT |
1503 | fsystem=None, options=None, |
1504 | origfsystem=None, migrate=0, | |
1505 | order=-1, fsck=-1, format=0, | |
69395171 | 1506 | badblocks = 0, bytesPerInode=4096, fsprofile=None): |
082638e9 MT |
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, " | |
d8ea2283 MT |
1536 | "but has been added to fsset with format " |
1537 | "flag on" % fsystem.getName()) | |
082638e9 MT |
1538 | self.format = format |
1539 | self.badblocks = badblocks | |
1540 | self.bytesPerInode = bytesPerInode | |
69395171 | 1541 | self.fsprofile = fsprofile |
082638e9 MT |
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,), | |
d8ea2283 MT |
1550 | readOnly = readOnly, |
1551 | bindMount = isinstance(self.device, | |
1552 | BindMountDevice), | |
1553 | instroot = chroot) | |
082638e9 MT |
1554 | else: |
1555 | self.fsystem.mount(device, "%s" % (self.mountpoint,), | |
d8ea2283 MT |
1556 | readOnly = readOnly, |
1557 | bindMount = isinstance(self.device, | |
1558 | BindMountDevice), | |
1559 | instroot = chroot) | |
082638e9 MT |
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 | ||
6a9a6ae9 MT |
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 | ||
082638e9 MT |
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 | ||
6a9a6ae9 MT |
1611 | def getUuid (self): |
1612 | return isys.readFSUuid(self.device.getDevice()) | |
1613 | ||
082638e9 MT |
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" | |
d8ea2283 MT |
1624 | " fsystem: %(fsystem)s format: %(format)s\n" |
1625 | " ismounted: %(mounted)s options: '%(options)s'\n" | |
6a9a6ae9 MT |
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}) | |
082638e9 MT |
1631 | return str |
1632 | ||
99237823 MT |
1633 | |
1634 | class Device: | |
082638e9 MT |
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 = "" | |
99237823 | 1641 | |
082638e9 MT |
1642 | def getComment (self): |
1643 | return "" | |
99237823 | 1644 | |
082638e9 MT |
1645 | def getDevice (self, asBoot = 0): |
1646 | return self.device | |
99237823 | 1647 | |
082638e9 MT |
1648 | def setupDevice (self, chroot='/', devPrefix='/dev'): |
1649 | return self.device | |
99237823 | 1650 | |
082638e9 MT |
1651 | def cleanupDevice (self, chroot, devPrefix='/dev'): |
1652 | pass | |
99237823 | 1653 | |
082638e9 MT |
1654 | def solidify (self): |
1655 | pass | |
99237823 | 1656 | |
082638e9 MT |
1657 | def getName(self): |
1658 | return self.__class__.__name__ | |
99237823 | 1659 | |
082638e9 MT |
1660 | def getLabel(self): |
1661 | try: | |
69395171 | 1662 | return isys.readFSLabel(self.setupDevice()) |
082638e9 MT |
1663 | except: |
1664 | return "" | |
99237823 | 1665 | |
082638e9 MT |
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" | |
99237823 | 1670 | |
082638e9 MT |
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 | |
99237823 | 1676 | |
082638e9 MT |
1677 | def getDeviceOptions(self): |
1678 | return self.deviceOptions | |
99237823 MT |
1679 | |
1680 | class DevDevice(Device): | |
082638e9 MT |
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 | |
99237823 | 1686 | |
082638e9 MT |
1687 | def getDevice(self, asBoot = 0): |
1688 | return self.device | |
99237823 | 1689 | |
082638e9 MT |
1690 | def setupDevice(self, chroot='/', devPrefix='/dev'): |
1691 | return "/dev/%s" %(self.getDevice(),) | |
99237823 MT |
1692 | |
1693 | ||
1694 | ext2 = fileSystemTypeGet("ext2") | |
1695 | ||
1696 | class PartitionDevice(Device): | |
082638e9 MT |
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 | |
99237823 | 1702 | |
082638e9 MT |
1703 | def setupDevice(self, chroot="/", devPrefix='/dev'): |
1704 | path = '%s/%s' % (devPrefix, self.getDevice(),) | |
1705 | return path | |
99237823 MT |
1706 | |
1707 | class PartedPartitionDevice(PartitionDevice): | |
082638e9 MT |
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 | |
99237823 MT |
1724 | |
1725 | class BindMountDevice(Device): | |
082638e9 MT |
1726 | def __init__(self, directory): |
1727 | Device.__init__(self) | |
1728 | self.device = directory | |
99237823 | 1729 | |
082638e9 MT |
1730 | def setupDevice(self, chroot="/", devPrefix="/tmp"): |
1731 | return chroot + self.device | |
99237823 MT |
1732 | |
1733 | class SwapFileDevice(Device): | |
082638e9 MT |
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 " | |
d8ea2283 | 1752 | "required size is unknown.") |
082638e9 | 1753 | return file |
99237823 MT |
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): | |
082638e9 MT |
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) | |
99237823 MT |
1766 | |
1767 | class LoopbackDevice(Device): | |
082638e9 MT |
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) | |
99237823 MT |
1790 | |
1791 | def makeDevice(dev): | |
082638e9 MT |
1792 | device = DevDevice(dev) |
1793 | return device | |
99237823 MT |
1794 | |
1795 | def readFstab(pomona): | |
082638e9 MT |
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"), | |
d8ea2283 MT |
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")]) | |
082638e9 MT |
1822 | except TypeError: |
1823 | intf.messageWindow(_("Invalid Label"), | |
d8ea2283 MT |
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")]) | |
082638e9 MT |
1829 | |
1830 | sys.exit(0) | |
1831 | else: | |
1832 | log.warning("Duplicate labels for %s, but no intf so trying " | |
d8ea2283 | 1833 | "to continue" % (label,)) |
082638e9 MT |
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 " | |
d8ea2283 | 1897 | "could not be found on any file system", label) |
082638e9 MT |
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], | |
d8ea2283 | 1932 | origfsystem=fsystem) |
082638e9 MT |
1933 | if label: |
1934 | entry.setLabel(label) | |
1935 | fsset.add(entry) | |
1936 | return fsset | |
99237823 MT |
1937 | |
1938 | def getDevFD(device): | |
082638e9 MT |
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 | |
99237823 MT |
1948 | |
1949 | def isValidExt2(device): | |
082638e9 MT |
1950 | fd = getDevFD(device) |
1951 | if fd == -1: | |
1952 | return 0 | |
99237823 | 1953 | |
082638e9 MT |
1954 | buf = os.read(fd, 2048) |
1955 | os.close(fd) | |
99237823 | 1956 | |
082638e9 MT |
1957 | if len(buf) != 2048: |
1958 | return 0 | |
99237823 | 1959 | |
082638e9 MT |
1960 | if struct.unpack("<H", buf[1080:1082]) == (0xef53,): |
1961 | return 1 | |
99237823 | 1962 | |
082638e9 | 1963 | return 0 |
99237823 MT |
1964 | |
1965 | def isValidXFS(device): | |
082638e9 MT |
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 | |
99237823 MT |
1980 | |
1981 | def isValidReiserFS(device): | |
082638e9 MT |
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 | |
99237823 MT |
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): | |
082638e9 | 2016 | rc = [] |
99237823 | 2017 | |
082638e9 MT |
2018 | if isValidXFS(device): |
2019 | rc.append("xfs") | |
99237823 | 2020 | |
082638e9 MT |
2021 | if isValidReiserFS(device): |
2022 | rc.append("reiserfs") | |
99237823 | 2023 | |
082638e9 MT |
2024 | if isValidExt2(device): |
2025 | if isys.ext2HasJournal(device): | |
2026 | rc.append("ext3") | |
2027 | rc.append("ext2") | |
99237823 MT |
2028 | |
2029 | ### XXX FIXME: need to check for swap | |
2030 | ||
082638e9 | 2031 | return rc |
99237823 MT |
2032 | |
2033 | def allocateLoopback(file): | |
082638e9 MT |
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 | |
99237823 MT |
2047 | |
2048 | def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint): | |
082638e9 MT |
2049 | if windowCreator: |
2050 | w = windowCreator(_("Formatting"), | |
d8ea2283 | 2051 | _("Formatting %s file system...") % (mntpoint,), 100) |
082638e9 MT |
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 | |
99237823 MT |
2127 | |
2128 | # copy and paste job from booty/bootloaderInfo.py... | |
2129 | def getDiskPart(dev): | |
082638e9 MT |
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) |