144 lines
4.4 KiB
Python
144 lines
4.4 KiB
Python
|
#
|
||
|
# Copyright (C) 2021 Red Hat, Inc. All rights reserved.
|
||
|
#
|
||
|
# This program 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 2 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
#
|
||
|
import os
|
||
|
from pyanaconda.core.configuration.anaconda import conf
|
||
|
|
||
|
|
||
|
def set_system_root(path):
|
||
|
"""Change the OS root path.
|
||
|
|
||
|
The path defined by conf.target.system_root will be bind mounted at the given
|
||
|
path, so conf.target.system_root can be used to access the root of the new OS.
|
||
|
|
||
|
We always call it after the root device is mounted at conf.target.physical_root
|
||
|
to set the physical root as the current system root.
|
||
|
|
||
|
Then, it can be used by Payload subclasses which install operating systems to
|
||
|
non-default roots.
|
||
|
|
||
|
If the given path is None, then conf.target.system_root is only unmounted.
|
||
|
|
||
|
:param path: the new OS root path or None
|
||
|
:type path: str or None
|
||
|
"""
|
||
|
from pyanaconda.core.util import execWithRedirect
|
||
|
|
||
|
sysroot = conf.target.system_root
|
||
|
|
||
|
if sysroot == path:
|
||
|
return
|
||
|
|
||
|
# Unmount the mount point if necessary.
|
||
|
rc = execWithRedirect("findmnt", ["-rn", sysroot])
|
||
|
|
||
|
if rc == 0:
|
||
|
execWithRedirect("mount", ["--make-rprivate", sysroot])
|
||
|
execWithRedirect("umount", ["--recursive", sysroot])
|
||
|
|
||
|
if not path:
|
||
|
return
|
||
|
|
||
|
# Create a directory for the mount point.
|
||
|
if not os.path.exists(sysroot):
|
||
|
make_directories(sysroot)
|
||
|
|
||
|
# Mount the mount point.
|
||
|
rc = execWithRedirect("mount", ["--rbind", path, sysroot])
|
||
|
|
||
|
if rc != 0:
|
||
|
raise OSError("Failed to mount sysroot to {}.".format(path))
|
||
|
|
||
|
|
||
|
def make_directories(directory):
|
||
|
"""Make a directory and all of its parents. Don't fail if part of the path already exists.
|
||
|
|
||
|
:param str directory: The directory path to create
|
||
|
"""
|
||
|
os.makedirs(directory, 0o755, exist_ok=True)
|
||
|
|
||
|
|
||
|
def get_mount_paths(devnode):
|
||
|
"""Given a device node, return a list of all active mountpoints."""
|
||
|
devno = os.stat(devnode).st_rdev
|
||
|
majmin = "%d:%d" % (os.major(devno), os.minor(devno))
|
||
|
mountinfo = (line.split() for line in open("/proc/self/mountinfo"))
|
||
|
return [info[4] for info in mountinfo if info[2] == majmin]
|
||
|
|
||
|
|
||
|
def open_with_perm(path, mode='r', perm=0o777, **kwargs):
|
||
|
"""Open a file with the given permission bits.
|
||
|
|
||
|
This is more or less the same as using os.open(path, flags, perm), but
|
||
|
with the builtin open() semantics and return type instead of a file
|
||
|
descriptor.
|
||
|
|
||
|
:param str path: The path of the file to be opened
|
||
|
:param str mode: The same thing as the mode argument to open()
|
||
|
:param int perm: What permission bits to use if creating a new file
|
||
|
:return: Opened file-like object
|
||
|
"""
|
||
|
def _opener(path_to_open, open_flags):
|
||
|
return os.open(path_to_open, open_flags, perm)
|
||
|
|
||
|
return open(path, mode, opener=_opener, **kwargs)
|
||
|
|
||
|
|
||
|
def join_paths(path, *paths):
|
||
|
"""Always join paths.
|
||
|
|
||
|
The os.path.join() function has a drawback when second path is absolute. In that case it will
|
||
|
instead return the second path only.
|
||
|
|
||
|
:param path: first path we want to join
|
||
|
:param paths: paths we want to merge
|
||
|
:returns: return path created from all the input paths
|
||
|
:rtype: str
|
||
|
"""
|
||
|
if len(paths) == 0:
|
||
|
return path
|
||
|
|
||
|
new_paths = []
|
||
|
for p in paths:
|
||
|
new_paths.append(p.lstrip(os.path.sep))
|
||
|
|
||
|
return os.path.join(path, *new_paths)
|
||
|
|
||
|
|
||
|
def touch(file_path):
|
||
|
"""Create an empty file.
|
||
|
|
||
|
This mirrors how touch works - it does not throw an error if the given path exists,
|
||
|
even when the path points to a directory.
|
||
|
|
||
|
:param str file_path: Path to the file to create
|
||
|
"""
|
||
|
if not os.path.exists(file_path):
|
||
|
os.mknod(file_path)
|
||
|
|
||
|
|
||
|
def set_mode(file_path, perm=0o600):
|
||
|
"""Set file permission to a given file
|
||
|
|
||
|
In case the file doesn't exists - create it.
|
||
|
|
||
|
:param str file_path: Path to a file
|
||
|
:param int perm: File permissions in format of os.chmod()
|
||
|
"""
|
||
|
if not os.path.exists(file_path):
|
||
|
touch(file_path)
|
||
|
os.chmod(file_path, perm)
|