]> glassweightruler.freedombox.rocks Git - waydroid.git/blob - tools/helpers/mount.py
Add first-launch command
[waydroid.git] / tools / helpers / mount.py
1 # Copyright 2021 Oliver Smith
2 # SPDX-License-Identifier: GPL-3.0-or-later
3 import os
4 import tools.helpers.run
5
6
7 def ismount(folder):
8 """
9 Ismount() implementation, that works for mount --bind.
10 Workaround for: https://bugs.python.org/issue29707
11 """
12 folder = os.path.realpath(os.path.realpath(folder))
13 with open("/proc/mounts", "r") as handle:
14 for line in handle:
15 words = line.split()
16 if len(words) >= 2 and words[1] == folder:
17 return True
18 if words[0] == folder:
19 return True
20 return False
21
22
23 def bind(args, source, destination, create_folders=True, umount=False):
24 """
25 Mount --bind a folder and create necessary directory structure.
26 :param umount: when destination is already a mount point, umount it first.
27 """
28 # Check/umount destination
29 if ismount(destination):
30 if umount:
31 umount_all(args, destination)
32 else:
33 return
34
35 # Check/create folders
36 for path in [source, destination]:
37 if os.path.exists(path):
38 continue
39 if create_folders:
40 tools.helpers.run.user(args, ["mkdir", "-p", path])
41 else:
42 raise RuntimeError("Mount failed, folder does not exist: " +
43 path)
44
45 # Actually mount the folder
46 tools.helpers.run.user(args, ["mount", "-o", "bind", source, destination])
47
48 # Verify, that it has worked
49 if not ismount(destination):
50 raise RuntimeError("Mount failed: " + source + " -> " + destination)
51
52
53 def bind_file(args, source, destination, create_folders=False):
54 """
55 Mount a file with the --bind option, and create the destination file,
56 if necessary.
57 """
58 # Skip existing mountpoint
59 if ismount(destination):
60 return
61
62 # Create empty file
63 if not os.path.exists(destination):
64 if create_folders:
65 dir = os.path.dirname(destination)
66 if not os.path.isdir(dir):
67 tools.helpers.run.user(args, ["mkdir", "-p", dir])
68
69 tools.helpers.run.user(args, ["touch", destination])
70
71 # Mount
72 tools.helpers.run.user(args, ["mount", "-o", "bind", source,
73 destination])
74
75
76 def umount_all_list(prefix, source="/proc/mounts"):
77 """
78 Parses `/proc/mounts` for all folders beginning with a prefix.
79 :source: can be changed for testcases
80 :returns: a list of folders, that need to be umounted
81 """
82 ret = []
83 prefix = os.path.realpath(prefix)
84 with open(source, "r") as handle:
85 for line in handle:
86 words = line.split()
87 if len(words) < 2:
88 raise RuntimeError("Failed to parse line in " + source + ": " +
89 line)
90 mountpoint = words[1]
91 if mountpoint.startswith(prefix):
92 # Remove "\040(deleted)" suffix (#545)
93 deleted_str = r"\040(deleted)"
94 if mountpoint.endswith(deleted_str):
95 mountpoint = mountpoint[:-len(deleted_str)]
96 ret.append(mountpoint)
97 ret.sort(reverse=True)
98 return ret
99
100
101 def umount_all(args, folder):
102 """
103 Umount all folders, that are mounted inside a given folder.
104 """
105 for mountpoint in umount_all_list(folder):
106 tools.helpers.run.user(args, ["umount", mountpoint])
107 if ismount(mountpoint):
108 raise RuntimeError("Failed to umount: " + mountpoint)
109
110 def mount(args, source, destination, create_folders=True, umount=False, readonly=True):
111 """
112 Mount and create necessary directory structure.
113 :param umount: when destination is already a mount point, umount it first.
114 """
115 # Check/umount destination
116 if ismount(destination):
117 if umount:
118 umount_all(args, destination)
119 else:
120 return
121
122 # Check/create folders
123 if not os.path.exists(destination):
124 if create_folders:
125 tools.helpers.run.user(args, ["mkdir", "-p", destination])
126 else:
127 raise RuntimeError("Mount failed, folder does not exist: " +
128 destination)
129
130 # Actually mount the folder
131 tools.helpers.run.user(args, ["mount", source, destination])
132 if readonly:
133 tools.helpers.run.user(args, ["mount", "-o", "remount,ro", source, destination])
134
135 # Verify, that it has worked
136 if not ismount(destination):
137 raise RuntimeError("Mount failed: " + source + " -> " + destination)