forked from Mirror/pmbootstrap
Do not install git in the native chroot and use it from there. Remove the chown_to_user argument from pmb.helpers.git.clone(), the resulting dir is now always owned by the user. While at it, refactor the function and display the clone URL. Previously we had cloned aports_upstream (from Alpine) with chown_to_user=False (legacy) and pmaports with chown_to_user=True. pmb.helpers.git.rev_parse() would only work after chown_to_user=True. Check if git is installed in "pmbootstrap init", and remove the same check from rev_parse(). Add a new work dir version, that checks for git and changes ownership of already checked out aports_upstream to the host system's user. When creating a new work dir, create cache_git instead of cache_http. cache_http is created on demand already, with proper permissions. But cache_git must be created, otherwise pmb.helpers.mount.bind will create it as root. This is in preparation for the "pmbootstrap pull" feature, as it allows using the host system's git in all new code paths. We will be able to handle repositories even if they were cloned outside of the work dir (which we do in a few CI scripts for example). Related: #1858
212 lines
7.5 KiB
Python
212 lines
7.5 KiB
Python
"""
|
|
Copyright 2020 Oliver Smith
|
|
|
|
This file is part of pmbootstrap.
|
|
|
|
pmbootstrap is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
pmbootstrap is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
import logging
|
|
import os
|
|
import re
|
|
import pmb.chroot
|
|
import pmb.config
|
|
import pmb.config.init
|
|
import pmb.helpers.pmaports
|
|
import pmb.helpers.run
|
|
|
|
|
|
def folder_size(args, path):
|
|
"""
|
|
Run `du` to calculate the size of a folder (this is less code and
|
|
faster than doing the same task in pure Python). This result is only
|
|
approximatelly right, but good enough for pmbootstrap's use case (#760).
|
|
|
|
:returns: folder size in bytes
|
|
"""
|
|
output = pmb.helpers.run.root(args, ["du", "--summarize",
|
|
"--block-size=1",
|
|
path], output_return=True)
|
|
|
|
# Only look at last line to filter out sudo garbage (#1766)
|
|
last_line = output.split("\n")[-2]
|
|
|
|
ret = int(last_line.split("\t")[0])
|
|
return ret
|
|
|
|
|
|
def check_grsec(args):
|
|
"""
|
|
Check if the current kernel is based on the grsec patchset, and if
|
|
the chroot_deny_chmod option is enabled. Raise an exception in that
|
|
case, with a link to the issue. Otherwise, do nothing.
|
|
"""
|
|
path = "/proc/sys/kernel/grsecurity/chroot_deny_chmod"
|
|
if not os.path.exists(path):
|
|
return
|
|
|
|
raise RuntimeError("You're running a kernel based on the grsec"
|
|
" patchset. This is not supported.")
|
|
|
|
|
|
def check_binfmt_misc(args):
|
|
"""
|
|
Check if the 'binfmt_misc' module is loaded by checking, if
|
|
/proc/sys/fs/binfmt_misc/ exists. If it exists, then do nothing.
|
|
Otherwise, raise an exception pointing to user to the Wiki.
|
|
"""
|
|
path = "/proc/sys/fs/binfmt_misc/status"
|
|
if os.path.exists(path):
|
|
return
|
|
|
|
link = "https://postmarketos.org/binfmt_misc"
|
|
raise RuntimeError("It appears that your system has not loaded the"
|
|
" module 'binfmt_misc'. This is required to run"
|
|
" foreign architecture programs with QEMU (eg."
|
|
" armhf on x86_64):\n See: <" + link + ">")
|
|
|
|
|
|
def migrate_success(args, version):
|
|
logging.info("Migration to version " + str(version) + " done")
|
|
with open(args.work + "/version", "w") as handle:
|
|
handle.write(str(version) + "\n")
|
|
|
|
|
|
def migrate_work_folder(args):
|
|
# Read current version
|
|
current = 0
|
|
path = args.work + "/version"
|
|
if os.path.exists(path):
|
|
with open(path, "r") as f:
|
|
current = int(f.read().rstrip())
|
|
|
|
# Compare version, print warning or do nothing
|
|
required = pmb.config.work_version
|
|
if current == required:
|
|
return
|
|
logging.info("WARNING: Your work folder version needs to be migrated"
|
|
" (from version " + str(current) + " to " + str(required) +
|
|
")!")
|
|
|
|
# 0 => 1
|
|
if current == 0:
|
|
# Ask for confirmation
|
|
logging.info("Changelog:")
|
|
logging.info("* Building chroots have a different username (#709)")
|
|
logging.info("Migration will do the following:")
|
|
logging.info("* Zap your chroots")
|
|
logging.info("* Adjust '" + args.work + "/config_abuild/abuild.conf'")
|
|
if not pmb.helpers.cli.confirm(args):
|
|
raise RuntimeError("Aborted.")
|
|
|
|
# Zap and update abuild.conf
|
|
pmb.chroot.zap(args, False)
|
|
conf = args.work + "/config_abuild/abuild.conf"
|
|
if os.path.exists(conf):
|
|
pmb.helpers.run.root(args, ["sed", "-i",
|
|
"s./home/user/./home/pmos/.g", conf])
|
|
# Update version file
|
|
migrate_success(args, 1)
|
|
current = 1
|
|
|
|
# 1 => 2
|
|
if current == 1:
|
|
# Ask for confirmation
|
|
logging.info("Changelog:")
|
|
logging.info("* Fix: cache_distfiles was writable for everyone")
|
|
logging.info("Migration will do the following:")
|
|
logging.info("* Fix permissions of '" + args.work +
|
|
"/cache_distfiles'")
|
|
if not pmb.helpers.cli.confirm(args):
|
|
raise RuntimeError("Aborted.")
|
|
|
|
# Fix permissions
|
|
dir = "/var/cache/distfiles"
|
|
for cmd in [["chown", "-R", "root:abuild", dir],
|
|
["chmod", "-R", "664", dir],
|
|
["chmod", "a+X", dir]]:
|
|
pmb.chroot.root(args, cmd)
|
|
migrate_success(args, 2)
|
|
current = 2
|
|
|
|
if current == 2:
|
|
# Ask for confirmation
|
|
logging.info("Changelog:")
|
|
logging.info("* Device chroots have a different user UID (#1576)")
|
|
logging.info("Migration will do the following:")
|
|
logging.info("* Zap your chroots")
|
|
if not pmb.helpers.cli.confirm(args):
|
|
raise RuntimeError("Aborted.")
|
|
|
|
# Zap chroots
|
|
pmb.chroot.zap(args, False)
|
|
|
|
# Update version file
|
|
migrate_success(args, 3)
|
|
current = 3
|
|
|
|
if current == 3:
|
|
# Ask for confirmation
|
|
path = args.work + "/cache_git"
|
|
logging.info("Changelog:")
|
|
logging.info("* pmbootstrap clones repositories with host system's")
|
|
logging.info(" 'git' instead of using it from an Alpine chroot")
|
|
logging.info("Migration will do the following:")
|
|
logging.info("* Check if 'git' is installed")
|
|
logging.info("* Change ownership to your user: " + path)
|
|
if not pmb.helpers.cli.confirm(args):
|
|
raise RuntimeError("Aborted.")
|
|
|
|
# Require git, set cache_git ownership
|
|
pmb.config.init.require_programs()
|
|
if os.path.exists(path):
|
|
uid_gid = "{}:{}".format(os.getuid(), os.getgid())
|
|
pmb.helpers.run.root(args, ["chown", "-R", uid_gid, path])
|
|
else:
|
|
os.makedirs(path, 0o700, True)
|
|
|
|
# Update version file
|
|
migrate_success(args, 4)
|
|
current = 4
|
|
|
|
# Can't migrate, user must delete it
|
|
if current != required:
|
|
raise RuntimeError("Sorry, we can't migrate that automatically. Please"
|
|
" run 'pmbootstrap shutdown', then delete your"
|
|
" current work folder manually ('sudo rm -rf " +
|
|
args.work + "') and start over with 'pmbootstrap"
|
|
" init'. All your binary packages and caches will"
|
|
" be lost.")
|
|
|
|
|
|
def validate_hostname(hostname):
|
|
"""
|
|
Check whether the string is a valid hostname, according to
|
|
<http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names>
|
|
"""
|
|
# Check length
|
|
if len(hostname) > 63:
|
|
logging.fatal("ERROR: Hostname '" + hostname + "' is too long.")
|
|
return False
|
|
|
|
# Check that it only contains valid chars
|
|
if not re.match("^[0-9a-z-]*$", hostname):
|
|
logging.fatal("ERROR: Hostname must only contain letters (a-z),"
|
|
" digits (0-9) or minus signs (-)")
|
|
return False
|
|
|
|
# Check that doesn't begin or end with a minus sign
|
|
if hostname[:1] == "-" or hostname[-1:] == "-":
|
|
logging.fatal("ERROR: Hostname must not begin or end with a minus sign")
|
|
return False
|
|
return True
|