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