1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
# Copyright (C) 2012-2013 Codethink Limited
#
# 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; version 2 of the License.
#
# 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import re
def create_image(runcmd, image_name, size): # pragma: no cover
# FIXME a pure python implementation may be better
runcmd(['dd', 'if=/dev/zero', 'of=' + image_name, 'bs=1',
'seek=%d' % size, 'count=0'])
def partition_image(runcmd, image_name): # pragma: no cover
# FIXME make this more flexible with partitioning options
runcmd(['sfdisk', image_name], feed_stdin='1,,83,*\n')
def setup_device_mapping(runcmd, image_name): # pragma: no cover
findstart = re.compile(r"start=\s+(\d+),")
out = runcmd(['sfdisk', '-d', image_name])
for line in out.splitlines():
match = findstart.search(line)
if match is None:
continue
start = int(match.group(1)) * 512
if start != 0:
break
device = runcmd(['losetup', '--show', '-o', str(start), '-f', image_name])
return device.strip()
def create_fs(runcmd, partition): # pragma: no cover
runcmd(['mkfs.btrfs', '-L', 'baserock', partition])
def mount(runcmd, partition, mount_point): # pragma: no cover
if not os.path.exists(mount_point):
os.mkdir(mount_point)
runcmd(['mount', partition, mount_point])
def unmount(runcmd, mount_point): # pragma: no cover
runcmd(['umount', mount_point])
def undo_device_mapping(runcmd, image_name): # pragma: no cover
out = runcmd(['losetup', '-j', image_name])
for line in out.splitlines():
i = line.find(':')
device = line[:i]
runcmd(['losetup', '-d', device])
def invert_paths(tree_walker, paths):
'''List paths from `tree_walker` that are not in `paths`.
Given a traversal of a tree and a set of paths separated by os.sep,
return the files and directories that are not part of the set of
paths, culling directories that do not need to be recursed into,
if the traversal supports this.
`tree_walker` is expected to follow similar behaviour to `os.walk()`.
This function will remove directores from the ones listed, to avoid
traversing into these subdirectories, if it doesn't need to.
As such, if a directory is returned, it is implied that its contents
are also not in the set of paths.
If the tree walker does not support culling the traversal this way,
such as `os.walk(root, topdown=False)`, then the contents will also
be returned.
The purpose for this is to list the directories that can be made
read-only, such that it would leave everything in paths writable.
Each path in `paths` is expected to begin with the same path as
yielded by the tree walker.
'''
def is_subpath(prefix, path):
prefix_components = prefix.split(os.sep)
path_components = path.split(os.sep)
return path_components[:len(prefix_components)] == prefix_components
for dirpath, dirnames, filenames in tree_walker:
dn_copy = list(dirnames)
for subdir in dn_copy:
subdirpath = os.path.join(dirpath, subdir)
if any(p == subdirpath for p in paths):
# Subdir is an exact match for a path
# Don't recurse into it, so remove from list
# Don't yield it, since we don't return listed paths
dirnames.remove(subdir)
elif any(is_subpath(subdirpath, p) for p in paths):
# This directory is a parent directory of one
# of our paths
# Recurse into it, so don't remove it from the list
# Don't yield it, since we don't return listed paths
pass
else:
# This directory is neither one marked for writing,
# nor a parent of a file marked for writing
# Don't recurse, so remove it from the list
# Yield it, since we return listed paths
dirnames.remove(subdir)
yield subdirpath
for filename in filenames:
fullpath = os.path.join(dirpath, filename)
if any(is_subpath(p, fullpath) for p in paths):
# The file path is a child of one of the paths
# or is equal.
# Don't yield because either it is one of the specified
# paths, or is a file in a directory specified by a path
pass
else:
yield fullpath
|