Viewing file:
DistUpgradeAufs.py (9.41 KB) -rw-r--r--Select action/file-type:

(
+) |

(
+) |

(
+) |
Code (
+) |
Session (
+) |

(
+) |
SDB (
+) |

(
+) |

(
+) |

(
+) |

(
+) |

(
+) |
import string
import logging
import os
import os.path
import subprocess
import tempfile
# dirs that the packages will touch
systemdirs = ["/bin",
"/boot",
"/etc",
"/initrd",
"/lib",
"/lib32", # ???
"/sbin",
"/usr",
"/var"]
def aufsOptionsAndEnvironmentSetup(options, config):
""" setup the environment based on the config and options
It will use
config("Aufs","Enabled") - to show if its enabled
and
config("Aufs","RWDir") - for the writable overlay dir
"""
logging.debug("aufsOptionsAndEnvironmentSetup()")
# enabled from the commandline (full overlay by default)
if options and options.useAufs:
logging.debug("enabling full overlay from commandline")
config.set("Aufs","Enabled", "True")
config.set("Aufs","EnableFullOverlay","True")
# setup environment based on config
tmprw = tempfile.mkdtemp(prefix="upgrade-rw-")
aufs_rw_dir = config.getWithDefault("Aufs","RWDir", tmprw)
logging.debug("using '%s' as aufs_rw_dir" % aufs_rw_dir)
os.environ["RELEASE_UPGRADE_AUFS_RWDIR"] = aufs_rw_dir
config.set("Aufs","RWDir",aufs_rw_dir)
# now the chroot tmpdir
tmpchroot = tempfile.mkdtemp(prefix="upgrade-chroot-")
os.chmod(tmpchroot, 0755)
aufs_chroot_dir = config.getWithDefault("Aufs","ChrootDir", tmpchroot)
logging.debug("using '%s' as aufs chroot dir" % aufs_chroot_dir)
if config.getWithDefault("Aufs","EnableFullOverlay", False):
logging.debug("enabling aufs full overlay (from config)")
config.set("Aufs","Enabled", "True")
os.environ["RELEASE_UPGRADE_USE_AUFS_FULL_OVERLAY"] = "1"
if config.getWithDefault("Aufs","EnableChrootOverlay",False):
logging.debug("enabling aufs chroot overlay")
config.set("Aufs","Enabled", "True")
os.environ["RELEASE_UPGRADE_USE_AUFS_CHROOT"] = aufs_chroot_dir
if config.getWithDefault("Aufs","EnableChrootRsync", False):
logging.debug("enable aufs chroot rsync back to real system")
os.environ["RELEASE_UPGRADE_RSYNC_AUFS_CHROOT"] = "1"
def _bindMount(from_dir, to_dir, rbind=False):
" helper that bind mounts a given dir to a new place "
if not os.path.exists(to_dir):
os.makedirs(to_dir)
if rbind:
bind = "--rbind"
else:
bind = "--bind"
cmd = ["mount",bind, from_dir, to_dir]
logging.debug("cmd: %s" % cmd)
res = subprocess.call(cmd)
if res != 0:
# FIXME: revert already mounted stuff
logging.error("Failed to bind mount from '%s' to '%s'" % (from_dir, to_dir))
return False
return True
def _aufsOverlayMount(target, rw_dir, chroot_dir="/"):
"""
helper that takes a target dir and mounts a rw dir over it, e.g.
/var , /tmp/upgrade-rw
"""
if not os.path.exists(rw_dir+target):
os.makedirs(rw_dir+target)
if not os.path.exists(chroot_dir+target):
os.makedirs(chroot_dir+target)
cmd = ["mount",
"-t","aufs",
"-o","br:%s:%s=ro" % (rw_dir+target, target),
"none",
chroot_dir+target]
res = subprocess.call(cmd)
if res != 0:
# FIXME: revert already mounted stuff
logging.error("Failed to mount rw aufs overlay for '%s'" % target)
return False
logging.debug("cmd '%s' return '%s' " % (cmd, res))
return True
def is_aufs_mount(dir):
" test if the given dir is already mounted with aufs overlay "
for line in open("/proc/mounts"):
(device, mountpoint, fstype, options, a, b) = line.split()
if device == "none" and fstype == "aufs" and mountpoint == dir:
return True
return False
def is_submount(mountpoint, systemdirs):
" helper: check if the given mountpoint is a submount of a systemdir "
logging.debug("is_submount: %s %s" % (mountpoint, systemdirs))
for d in systemdirs:
if not d.endswith("/"):
d += "/"
if mountpoint.startswith(d):
return True
return False
def is_real_fs(fs):
if fs.startswith("fuse"):
return False
if fs in ["rootfs","tmpfs","proc","fusectrl","aufs",
"devpts","binfmt_misc", "sysfs"]:
return False
return True
def doAufsChrootRsync(aufs_chroot_dir):
"""
helper that rsyncs the changes in the aufs chroot back to the
real system
"""
for d in systemdirs:
if not os.path.exists(d):
continue
# its important to have the "/" at the end of source
# and dest so that rsync knows what to do
cmd = ["rsync","-aHAX","--del","-v", "--progress",
"/%s/%s/" % (aufs_chroot_dir, d),
"/%s/" % d]
logging.debug("running: '%s'" % cmd)
ret = subprocess.call(cmd)
logging.debug("rsync back result for %s: %i" % (d, ret))
return True
def doAufsChroot(aufs_rw_dir, aufs_chroot_dir):
" helper that sets the chroot up and does chroot() into it "
if not setupAufsChroot(aufs_rw_dir, aufs_chroot_dir):
return False
os.chroot(aufs_chroot_dir)
os.chdir("/")
return True
def setupAufsChroot(rw_dir, chroot_dir):
" setup aufs chroot that is based on / but with a writable overlay "
# with the chroot aufs we can just rsync the changes back
# from the chroot dir to the real dirs
#
# (if we run rsync with --backup --backup-dir we could even
# create something vaguely rollbackable
# get the mount points before the aufs buisiness starts
mounts = open("/proc/mounts").read()
# aufs mount or bind mount required dirs
for d in os.listdir("/"):
d = os.path.join("/",d)
if os.path.isdir(d):
if d in systemdirs:
logging.debug("bind mounting %s" % d)
if not _aufsOverlayMount(d, rw_dir, chroot_dir):
return False
else:
logging.debug("overlay mounting %s" % d)
if not _bindMount(d, chroot_dir+d, rbind=True):
return False
# create binds for the systemdirs
#needs_bind_mount = set()
for line in map(string.strip, mounts.split("\n")):
if not line: continue
(device, mountpoint, fstype, options, a, b) = line.split()
if (fstype != "aufs" and
not is_real_fs(fstype) and
is_submount(mountpoint, systemdirs)):
logging.debug("found %s that needs bind mount", mountpoint)
if not _bindMount(mountpoint, chroot_dir+mountpoint):
return False
return True
def setupAufs(rw_dir):
" setup aufs overlay over the rootfs "
# * we need to find a way to tell all the existing daemon
# to look into the new namespace. so probably something
# like a reboot is required and some hackery in initramfs-tools
# to ensure that we boot into a overlay ready system
# * this is much less of a issue with the aufsChroot code
logging.debug("setupAufs")
if not os.path.exists("/proc/mounts"):
logging.debug("no /proc/mounts, can not do aufs overlay")
return False
# verify that there are no submounts of a systemdir and collect
# the stuff that needs bind mounting (because a aufs does not
# include sub mounts)
needs_bind_mount = set()
needs_bind_mount.add("/var/cache/apt/archives")
for line in open("/proc/mounts"):
(device, mountpoint, fstype, options, a, b) = line.split()
if is_real_fs(fstype) and is_submount(mountpoint, systemdirs):
logging.warning("mountpoint %s submount of systemdir" % mountpoint)
return False
if (fstype != "aufs" and not is_real_fs(fstype) and is_submount(mountpoint, systemdirs)):
logging.debug("found %s that needs bind mount", mountpoint)
needs_bind_mount.add(mountpoint)
# aufs mounts do not support stacked filesystems, so
# if we mount /var we will loose the tmpfs stuff
# first bind mount varun and varlock into the tmpfs
for d in needs_bind_mount:
if not _bindMount(d, rw_dir+"/needs_bind_mount/"+d):
return False
# setup writable overlay into /tmp/upgrade-rw so that all
# changes are written there instead of the real fs
for d in systemdirs:
if not is_aufs_mount(d):
if not _aufsOverlayMount(d, rw_dir):
return False
# now bind back the tempfs to the original location
for d in needs_bind_mount:
if not _bindMount(rw_dir+"/needs_bind_mount/"+d, d):
return False
# The below information is only of historical relevance:
# now what we *could* do to apply the changes is to
# mount -o bind / /orig
# (bind is important, *not* rbind that includes submounts)
#
# This will give us the original "/" without the
# aufs rw overlay - *BUT* only if "/" is all on one parition
#
# then apply the diff (including the whiteouts) to /orig
# e.g. by "rsync -av /tmp/upgrade-rw /orig"
# "script that search for whiteouts and removes them"
# (whiteout files start with .wh.$name
# whiteout dirs with .wh..? - check with aufs man page)
return True
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
#print setupAufs("/tmp/upgrade-rw")
print setupAufsChroot("/tmp/upgrade-chroot-rw",
"/tmp/upgrade-chroot")