1 |
prometheanfire 14/03/27 23:08:43 |
2 |
|
3 |
Modified: 2013.2.2-CVE-2014-0134.patch |
4 |
Log: |
5 |
better patch |
6 |
|
7 |
(Portage version: 2.2.8-r1/cvs/Linux x86_64, signed Manifest commit with key 0x2471eb3e40ac5ac3) |
8 |
|
9 |
Revision Changes Path |
10 |
1.2 sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch |
11 |
|
12 |
file : http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch?rev=1.2&view=markup |
13 |
plain: http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch?rev=1.2&content-type=text/plain |
14 |
diff : http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch?r1=1.1&r2=1.2 |
15 |
|
16 |
Index: 2013.2.2-CVE-2014-0134.patch |
17 |
=================================================================== |
18 |
RCS file: /var/cvsroot/gentoo-x86/sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch,v |
19 |
retrieving revision 1.1 |
20 |
retrieving revision 1.2 |
21 |
diff -u -r1.1 -r1.2 |
22 |
--- 2013.2.2-CVE-2014-0134.patch 25 Mar 2014 20:51:33 -0000 1.1 |
23 |
+++ 2013.2.2-CVE-2014-0134.patch 27 Mar 2014 23:08:43 -0000 1.2 |
24 |
@@ -1,6 +1,6 @@ |
25 |
-From e2527e64f77ca0211c744908031cf056cb144f07 Mon Sep 17 00:00:00 2001 |
26 |
+From 25e761acd56d4c820273fc0245ada06c500c1637 Mon Sep 17 00:00:00 2001 |
27 |
From: David Ripton <dripton@××××××.com> |
28 |
-Date: Mon, 17 Mar 2014 22:18:05 -0400 |
29 |
+Date: Tue, 28 Jan 2014 16:38:51 -0500 |
30 |
Subject: [PATCH] Persist image format to a file, to prevent attacks based on |
31 |
changing it |
32 |
|
33 |
@@ -10,21 +10,48 @@ |
34 |
|
35 |
Now we store the image format to a 'disk.info' file, for Qcow2 and Raw |
36 |
images, and only autodetect for images that have never been written to |
37 |
-that file. Since glance takes the appropriate security precautions on |
38 |
-image upload, this should be sufficient and backward-compatible. |
39 |
+that file. |
40 |
|
41 |
-Manually resolved one conflict in nova/virt/libvirt/imagebackend.py: |
42 |
-snapshot_delete was removed in icehouse, but is still there in havana |
43 |
+SecurityImpact |
44 |
+ |
45 |
+Conflicts: |
46 |
+ nova/virt/libvirt/imagebackend.py |
47 |
+ |
48 |
+Manual tweaks to some mocking in test_imagebackend.py |
49 |
|
50 |
Change-Id: I2016efdb3f49a44ec4d677ac596eacc97871f30a |
51 |
+Co-authored-by: Nikola Dipanov <ndipanov@××××××.com> |
52 |
Closes-bug: #1221190 |
53 |
--- |
54 |
- nova/tests/virt/libvirt/test_imagebackend.py | 331 ++++++++++++++++++++++----- |
55 |
- nova/virt/libvirt/imagebackend.py | 57 ++++- |
56 |
- 2 files changed, 324 insertions(+), 64 deletions(-) |
57 |
+ nova/exception.py | 8 + |
58 |
+ nova/tests/virt/libvirt/test_imagebackend.py | 359 ++++++++++++++++++++++----- |
59 |
+ nova/tests/virt/libvirt/test_libvirt.py | 3 + |
60 |
+ nova/tests/virt/test_virt_drivers.py | 7 + |
61 |
+ nova/utils.py | 14 ++ |
62 |
+ nova/virt/libvirt/imagebackend.py | 76 +++++- |
63 |
+ 6 files changed, 403 insertions(+), 64 deletions(-) |
64 |
|
65 |
+diff --git a/nova/exception.py b/nova/exception.py |
66 |
+index 7c17803..770a700 100644 |
67 |
+--- a/nova/exception.py |
68 |
++++ b/nova/exception.py |
69 |
+@@ -449,6 +449,14 @@ class InvalidDiskFormat(Invalid): |
70 |
+ msg_fmt = _("Disk format %(disk_format)s is not acceptable") |
71 |
+ |
72 |
+ |
73 |
++class InvalidDiskInfo(Invalid): |
74 |
++ msg_fmt = _("Disk info file is invalid: %(reason)s") |
75 |
++ |
76 |
++ |
77 |
++class DiskInfoReadWriteFail(Invalid): |
78 |
++ msg_fmt = _("Failed to read or write disk info file: %(reason)s") |
79 |
++ |
80 |
++ |
81 |
+ class ImageUnacceptable(Invalid): |
82 |
+ msg_fmt = _("Image %(image_id)s is unacceptable: %(reason)s") |
83 |
+ |
84 |
diff --git a/nova/tests/virt/libvirt/test_imagebackend.py b/nova/tests/virt/libvirt/test_imagebackend.py |
85 |
-index 2455ec8..3df22fa 100644 |
86 |
+index 5bfa94d..5424f7b 100644 |
87 |
--- a/nova/tests/virt/libvirt/test_imagebackend.py |
88 |
+++ b/nova/tests/virt/libvirt/test_imagebackend.py |
89 |
@@ -16,6 +16,8 @@ |
90 |
@@ -36,7 +63,14 @@ |
91 |
|
92 |
import fixtures |
93 |
from oslo.config import cfg |
94 |
-@@ -31,7 +33,6 @@ CONF = cfg.CONF |
95 |
+@@ -27,13 +29,13 @@ |
96 |
+ from nova import test |
97 |
+ from nova.tests import fake_processutils |
98 |
+ from nova.tests.virt.libvirt import fake_libvirt_utils |
99 |
++from nova import utils |
100 |
+ from nova.virt.libvirt import imagebackend |
101 |
+ |
102 |
+ CONF = cfg.CONF |
103 |
|
104 |
|
105 |
class _ImageTestCase(object): |
106 |
@@ -44,7 +78,7 @@ |
107 |
|
108 |
def mock_create_image(self, image): |
109 |
def create_image(fn, base, size, *args, **kwargs): |
110 |
-@@ -40,10 +41,13 @@ class _ImageTestCase(object): |
111 |
+@@ -42,10 +44,13 @@ def create_image(fn, base, size, *args, **kwargs): |
112 |
|
113 |
def setUp(self): |
114 |
super(_ImageTestCase, self).setUp() |
115 |
@@ -58,10 +92,14 @@ |
116 |
self.NAME = 'fake.vm' |
117 |
self.TEMPLATE = 'template' |
118 |
|
119 |
-@@ -61,6 +65,70 @@ class _ImageTestCase(object): |
120 |
+@@ -63,6 +68,78 @@ def setUp(self): |
121 |
'nova.virt.libvirt.imagebackend.libvirt_utils', |
122 |
fake_libvirt_utils)) |
123 |
|
124 |
++ def fake_chown(path, owner_uid=None): |
125 |
++ return None |
126 |
++ self.stubs.Set(utils, 'chown', fake_chown) |
127 |
++ |
128 |
+ def tearDown(self): |
129 |
+ super(_ImageTestCase, self).tearDown() |
130 |
+ shutil.rmtree(self.INSTANCES_PATH) |
131 |
@@ -118,6 +156,10 @@ |
132 |
+ super(RawTestCase, self).setUp() |
133 |
+ self.stubs.Set(imagebackend.Raw, 'correct_format', lambda _: None) |
134 |
+ |
135 |
++ def fake_chown(path, owner_uid=None): |
136 |
++ return None |
137 |
++ self.stubs.Set(utils, 'chown', fake_chown) |
138 |
++ |
139 |
+ def prepare_mocks(self): |
140 |
+ fn = self.mox.CreateMockAnything() |
141 |
+ self.mox.StubOutWithMock(imagebackend.utils.synchronized, |
142 |
@@ -129,7 +171,7 @@ |
143 |
def test_cache(self): |
144 |
self.mox.StubOutWithMock(os.path, 'exists') |
145 |
if self.OLD_STYLE_INSTANCE_PATH: |
146 |
-@@ -128,66 +196,6 @@ class _ImageTestCase(object): |
147 |
+@@ -130,66 +207,6 @@ def test_cache_template_exists(self): |
148 |
|
149 |
self.mox.VerifyAll() |
150 |
|
151 |
@@ -196,7 +238,7 @@ |
152 |
def test_create_image(self): |
153 |
fn = self.prepare_mocks() |
154 |
fn(target=self.TEMPLATE_PATH, max_size=None, image_id=None) |
155 |
-@@ -222,16 +230,17 @@ class RawTestCase(_ImageTestCase, test.NoDBTestCase): |
156 |
+@@ -224,16 +241,21 @@ def test_create_image_extend(self): |
157 |
self.mox.VerifyAll() |
158 |
|
159 |
def test_correct_format(self): |
160 |
@@ -206,6 +248,10 @@ |
161 |
self.mox.StubOutWithMock(os.path, 'exists') |
162 |
self.mox.StubOutWithMock(imagebackend.images, 'qemu_img_info') |
163 |
|
164 |
++ def fake_chown(path, owner_uid=None): |
165 |
++ return None |
166 |
++ self.stubs.Set(utils, 'chown', fake_chown) |
167 |
++ |
168 |
os.path.exists(self.PATH).AndReturn(True) |
169 |
+ os.path.exists(self.DISK_INFO_PATH).AndReturn(False) |
170 |
info = self.mox.CreateMockAnything() |
171 |
@@ -215,7 +261,7 @@ |
172 |
self.mox.ReplayAll() |
173 |
|
174 |
image = self.image_class(self.INSTANCE, self.NAME, path=self.PATH) |
175 |
-@@ -239,6 +248,11 @@ class RawTestCase(_ImageTestCase, test.NoDBTestCase): |
176 |
+@@ -241,6 +263,11 @@ def test_correct_format(self): |
177 |
|
178 |
self.mox.VerifyAll() |
179 |
|
180 |
@@ -227,7 +273,18 @@ |
181 |
|
182 |
class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): |
183 |
SIZE = 1024 * 1024 * 1024 |
184 |
-@@ -259,6 +273,77 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): |
185 |
+@@ -251,6 +278,10 @@ def setUp(self): |
186 |
+ self.QCOW2_BASE = (self.TEMPLATE_PATH + |
187 |
+ '_%d' % (self.SIZE / (1024 * 1024 * 1024))) |
188 |
+ |
189 |
++ def fake_chown(path, owner_uid=None): |
190 |
++ return None |
191 |
++ self.stubs.Set(utils, 'chown', fake_chown) |
192 |
++ |
193 |
+ def prepare_mocks(self): |
194 |
+ fn = self.mox.CreateMockAnything() |
195 |
+ self.mox.StubOutWithMock(imagebackend.utils.synchronized, |
196 |
+@@ -261,6 +292,80 @@ def prepare_mocks(self): |
197 |
self.mox.StubOutWithMock(imagebackend.disk, 'extend') |
198 |
return fn |
199 |
|
200 |
@@ -240,6 +297,7 @@ |
201 |
+ os.path.exists(self.TEMPLATE_DIR).AndReturn(False) |
202 |
+ os.path.exists(self.INSTANCES_PATH).AndReturn(True) |
203 |
+ os.path.exists(self.PATH).AndReturn(False) |
204 |
++ os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
205 |
+ fn = self.mox.CreateMockAnything() |
206 |
+ fn(target=self.TEMPLATE_PATH) |
207 |
+ self.mox.ReplayAll() |
208 |
@@ -274,6 +332,7 @@ |
209 |
+ os.path.exists(self.INSTANCES_PATH).AndReturn(True) |
210 |
+ os.path.exists(self.TEMPLATE_DIR).AndReturn(True) |
211 |
+ os.path.exists(self.PATH).AndReturn(False) |
212 |
++ os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
213 |
+ fn = self.mox.CreateMockAnything() |
214 |
+ fn(target=self.TEMPLATE_PATH) |
215 |
+ self.mox.ReplayAll() |
216 |
@@ -292,6 +351,7 @@ |
217 |
+ os.path.exists(self.INSTANCES_PATH).AndReturn(True) |
218 |
+ os.path.exists(self.TEMPLATE_DIR).AndReturn(True) |
219 |
+ os.path.exists(self.PATH).AndReturn(False) |
220 |
++ os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
221 |
+ fn = self.mox.CreateMockAnything() |
222 |
+ fn(target=self.TEMPLATE_PATH) |
223 |
+ self.mox.ReplayAll() |
224 |
@@ -305,7 +365,7 @@ |
225 |
def test_create_image(self): |
226 |
fn = self.prepare_mocks() |
227 |
fn(max_size=None, target=self.TEMPLATE_PATH) |
228 |
-@@ -277,6 +362,8 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): |
229 |
+@@ -279,6 +384,8 @@ def test_create_image_with_size(self): |
230 |
self.mox.StubOutWithMock(os.path, 'exists') |
231 |
if self.OLD_STYLE_INSTANCE_PATH: |
232 |
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False) |
233 |
@@ -314,7 +374,7 @@ |
234 |
os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
235 |
os.path.exists(self.PATH).AndReturn(False) |
236 |
os.path.exists(self.PATH).AndReturn(False) |
237 |
-@@ -296,6 +383,8 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): |
238 |
+@@ -298,6 +405,8 @@ def test_create_image_too_small(self): |
239 |
self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size') |
240 |
if self.OLD_STYLE_INSTANCE_PATH: |
241 |
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False) |
242 |
@@ -323,7 +383,7 @@ |
243 |
os.path.exists(self.TEMPLATE_PATH).AndReturn(True) |
244 |
imagebackend.disk.get_disk_size(self.TEMPLATE_PATH |
245 |
).AndReturn(self.SIZE) |
246 |
-@@ -314,6 +403,8 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): |
247 |
+@@ -316,6 +425,8 @@ def test_generate_resized_backing_files(self): |
248 |
'get_disk_backing_file') |
249 |
if self.OLD_STYLE_INSTANCE_PATH: |
250 |
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False) |
251 |
@@ -332,7 +392,7 @@ |
252 |
os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
253 |
os.path.exists(self.PATH).AndReturn(True) |
254 |
|
255 |
-@@ -340,6 +431,9 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): |
256 |
+@@ -342,6 +453,9 @@ def test_qcow2_exists_and_has_no_backing_file(self): |
257 |
'get_disk_backing_file') |
258 |
if self.OLD_STYLE_INSTANCE_PATH: |
259 |
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False) |
260 |
@@ -342,7 +402,7 @@ |
261 |
os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
262 |
os.path.exists(self.PATH).AndReturn(True) |
263 |
|
264 |
-@@ -353,6 +447,55 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): |
265 |
+@@ -355,6 +469,53 @@ def test_qcow2_exists_and_has_no_backing_file(self): |
266 |
|
267 |
self.mox.VerifyAll() |
268 |
|
269 |
@@ -369,8 +429,7 @@ |
270 |
+ image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE) |
271 |
+ |
272 |
+ self.assertEqual(fake_processutils.fake_execute_get_log(), |
273 |
-+ ['chown root:root %s' % self.DISK_INFO_PATH, |
274 |
-+ 'fallocate -n -l 1 %s.fallocate_test' % self.PATH, |
275 |
++ ['fallocate -n -l 1 %s.fallocate_test' % self.PATH, |
276 |
+ 'fallocate -n -l %s %s' % (self.SIZE, self.PATH), |
277 |
+ 'fallocate -n -l %s %s' % (self.SIZE, self.PATH)]) |
278 |
+ |
279 |
@@ -392,13 +451,12 @@ |
280 |
+ # Testing fallocate is only called when user has write access. |
281 |
+ image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE) |
282 |
+ |
283 |
-+ self.assertEqual(fake_processutils.fake_execute_get_log(), |
284 |
-+ ['chown root:root %s' % self.DISK_INFO_PATH]) |
285 |
++ self.assertEqual(fake_processutils.fake_execute_get_log(), []) |
286 |
+ |
287 |
|
288 |
class LvmTestCase(_ImageTestCase, test.NoDBTestCase): |
289 |
VG = 'FakeVG' |
290 |
-@@ -429,6 +572,56 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase): |
291 |
+@@ -431,6 +592,58 @@ def _create_image_resize(self, sparse): |
292 |
|
293 |
self.mox.VerifyAll() |
294 |
|
295 |
@@ -408,6 +466,7 @@ |
296 |
+ os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False) |
297 |
+ os.path.exists(self.TEMPLATE_DIR).AndReturn(False) |
298 |
+ os.path.exists(self.PATH).AndReturn(False) |
299 |
++ os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
300 |
+ |
301 |
+ fn = self.mox.CreateMockAnything() |
302 |
+ fn(target=self.TEMPLATE_PATH) |
303 |
@@ -441,6 +500,7 @@ |
304 |
+ os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False) |
305 |
+ os.path.exists(self.TEMPLATE_DIR).AndReturn(True) |
306 |
+ os.path.exists(self.PATH).AndReturn(False) |
307 |
++ os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
308 |
+ fn = self.mox.CreateMockAnything() |
309 |
+ fn(target=self.TEMPLATE_PATH) |
310 |
+ self.mox.StubOutWithMock(imagebackend.fileutils, 'ensure_tree') |
311 |
@@ -455,13 +515,14 @@ |
312 |
def test_create_image(self): |
313 |
self._create_image(False) |
314 |
|
315 |
-@@ -594,6 +787,20 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase): |
316 |
+@@ -596,6 +809,21 @@ def test_cache_template_exists(self): |
317 |
|
318 |
self.mox.VerifyAll() |
319 |
|
320 |
+ def test_cache_base_dir_exists(self): |
321 |
+ self.mox.StubOutWithMock(os.path, 'exists') |
322 |
+ os.path.exists(self.TEMPLATE_DIR).AndReturn(True) |
323 |
++ os.path.exists(self.TEMPLATE_PATH).AndReturn(False) |
324 |
+ fn = self.mox.CreateMockAnything() |
325 |
+ fn(target=self.TEMPLATE_PATH) |
326 |
+ self.mox.StubOutWithMock(imagebackend.fileutils, 'ensure_tree') |
327 |
@@ -476,11 +537,89 @@ |
328 |
def test_create_image(self): |
329 |
fn = self.prepare_mocks() |
330 |
fn(max_size=None, rbd=self.rbd, target=self.TEMPLATE_PATH) |
331 |
+@@ -642,6 +870,13 @@ class BackendTestCase(test.NoDBTestCase): |
332 |
+ 'uuid': uuidutils.generate_uuid()} |
333 |
+ NAME = 'fake-name.suffix' |
334 |
+ |
335 |
++ def setUp(self): |
336 |
++ super(BackendTestCase, self).setUp() |
337 |
++ |
338 |
++ def fake_chown(path, owner_uid=None): |
339 |
++ return None |
340 |
++ self.stubs.Set(utils, 'chown', fake_chown) |
341 |
++ |
342 |
+ def get_image(self, use_cow, image_type): |
343 |
+ return imagebackend.Backend(use_cow).image(self.INSTANCE, |
344 |
+ self.NAME, |
345 |
+diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py |
346 |
+index ba842ad..ea8ac13 100644 |
347 |
+--- a/nova/tests/virt/libvirt/test_libvirt.py |
348 |
++++ b/nova/tests/virt/libvirt/test_libvirt.py |
349 |
+@@ -390,6 +390,9 @@ def fake_extend(image, size, use_cow=False): |
350 |
+ |
351 |
+ self.stubs.Set(libvirt_driver.disk, 'extend', fake_extend) |
352 |
+ |
353 |
++ self.stubs.Set(imagebackend.Image, 'resolve_driver_format', |
354 |
++ imagebackend.Image._get_driver_format) |
355 |
++ |
356 |
+ class FakeConn(): |
357 |
+ def getCapabilities(self): |
358 |
+ """Ensure standard capabilities being returned.""" |
359 |
+diff --git a/nova/tests/virt/test_virt_drivers.py b/nova/tests/virt/test_virt_drivers.py |
360 |
+index 832d9d6..8f1bd23 100644 |
361 |
+--- a/nova/tests/virt/test_virt_drivers.py |
362 |
++++ b/nova/tests/virt/test_virt_drivers.py |
363 |
+@@ -30,6 +30,7 @@ |
364 |
+ from nova.tests.virt.libvirt import fake_libvirt_utils |
365 |
+ from nova.virt import event as virtevent |
366 |
+ from nova.virt import fake |
367 |
++from nova.virt.libvirt import imagebackend |
368 |
+ |
369 |
+ LOG = logging.getLogger(__name__) |
370 |
+ |
371 |
+@@ -201,6 +202,12 @@ def setUp(self): |
372 |
+ fake.FakeVirtAPI()) |
373 |
+ self.ctxt = test_utils.get_test_admin_context() |
374 |
+ self.image_service = fake_image.FakeImageService() |
375 |
++ # NOTE(dripton): resolve_driver_format does some file reading and |
376 |
++ # writing and chowning that complicate testing too much by requiring |
377 |
++ # using real directories with proper permissions. Just stub it out |
378 |
++ # here; we test it in test_imagebackend.py |
379 |
++ self.stubs.Set(imagebackend.Image, 'resolve_driver_format', |
380 |
++ imagebackend.Image._get_driver_format) |
381 |
+ |
382 |
+ def _get_running_instance(self): |
383 |
+ instance_ref = test_utils.get_test_instance() |
384 |
+diff --git a/nova/utils.py b/nova/utils.py |
385 |
+index 599cb64..4757f3a 100755 |
386 |
+--- a/nova/utils.py |
387 |
++++ b/nova/utils.py |
388 |
+@@ -924,6 +924,20 @@ def temporary_chown(path, owner_uid=None): |
389 |
+ execute('chown', orig_uid, path, run_as_root=True) |
390 |
+ |
391 |
+ |
392 |
++def chown(path, owner_uid=None): |
393 |
++ """chown a path. |
394 |
++ |
395 |
++ :param owner_uid: UID of owner (defaults to current user) |
396 |
++ """ |
397 |
++ if owner_uid is None: |
398 |
++ owner_uid = os.getuid() |
399 |
++ |
400 |
++ orig_uid = os.stat(path).st_uid |
401 |
++ |
402 |
++ if orig_uid != owner_uid: |
403 |
++ execute('chown', owner_uid, path, run_as_root=True) |
404 |
++ |
405 |
++ |
406 |
+ @contextlib.contextmanager |
407 |
+ def tempdir(**kwargs): |
408 |
+ argdict = kwargs.copy() |
409 |
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py |
410 |
-index e900789..21c7641 100644 |
411 |
+index 51872cf..ed11c90 100644 |
412 |
--- a/nova/virt/libvirt/imagebackend.py |
413 |
+++ b/nova/virt/libvirt/imagebackend.py |
414 |
-@@ -88,6 +88,11 @@ class Image(object): |
415 |
+@@ -88,6 +88,11 @@ def __init__(self, source_type, driver_format, is_block_dev=False): |
416 |
self.is_block_dev = is_block_dev |
417 |
self.preallocate = False |
418 |
|
419 |
@@ -492,7 +631,7 @@ |
420 |
# NOTE(mikal): We need a lock directory which is shared along with |
421 |
# instance files, to cover the scenario where multiple compute nodes |
422 |
# are trying to create a base file at the same time |
423 |
-@@ -232,6 +237,46 @@ class Image(object): |
424 |
+@@ -232,6 +237,65 @@ def snapshot_extract(self, target, out_format): |
425 |
def snapshot_delete(self): |
426 |
raise NotImplementedError() |
427 |
|
428 |
@@ -508,38 +647,57 @@ |
429 |
+ |
430 |
+ See https://bugs.launchpad.net/nova/+bug/1221190 |
431 |
+ """ |
432 |
++ def _dict_from_line(line): |
433 |
++ if not line: |
434 |
++ return {} |
435 |
++ try: |
436 |
++ return jsonutils.loads(line) |
437 |
++ except (TypeError, ValueError) as e: |
438 |
++ msg = (_("Could not load line %(line)s, got error " |
439 |
++ "%(error)s") % |
440 |
++ {'line': line, 'error': unicode(e)}) |
441 |
++ raise exception.InvalidDiskInfo(reason=msg) |
442 |
++ |
443 |
+ @utils.synchronized(self.disk_info_path, external=False, |
444 |
+ lock_path=self.lock_path) |
445 |
+ def write_to_disk_info_file(): |
446 |
-+ with open(self.disk_info_path, "w") as disk_info_file: |
447 |
-+ disk_info_file.write('%s\n' % |
448 |
-+ jsonutils.dumps((self.path, driver_format))) |
449 |
-+ # Ensure the file is always owned by root so qemu can't write it. |
450 |
-+ utils.execute('chown', 'root:root', self.disk_info_path, |
451 |
-+ run_as_root=True) |
452 |
-+ |
453 |
-+ if (self.disk_info_path is not None and |
454 |
-+ os.path.exists(self.disk_info_path)): |
455 |
-+ with open(self.disk_info_path) as disk_info_file: |
456 |
++ # Use os.open to create it without group or world write permission. |
457 |
++ fd = os.open(self.disk_info_path, os.O_RDWR | os.O_CREAT, 0o644) |
458 |
++ with os.fdopen(fd, "r+") as disk_info_file: |
459 |
+ line = disk_info_file.read().rstrip() |
460 |
-+ try: |
461 |
-+ parts = jsonutils.loads(line) |
462 |
-+ except (TypeError, ValueError): |
463 |
-+ parts = [] |
464 |
-+ if len(parts) == 2: |
465 |
-+ (path, driver_format) = parts |
466 |
-+ if path == self.path: |
467 |
-+ return driver_format |
468 |
-+ driver_format = self._get_driver_format() |
469 |
-+ if self.disk_info_path is not None: |
470 |
-+ fileutils.ensure_tree(os.path.dirname(self.disk_info_path)) |
471 |
-+ write_to_disk_info_file() |
472 |
++ dct = _dict_from_line(line) |
473 |
++ if self.path in dct: |
474 |
++ msg = _("Attempted overwrite of an existing value.") |
475 |
++ raise exception.InvalidDiskInfo(reason=msg) |
476 |
++ dct.update({self.path: driver_format}) |
477 |
++ disk_info_file.seek(0) |
478 |
++ disk_info_file.truncate() |
479 |
++ disk_info_file.write('%s\n' % jsonutils.dumps(dct)) |
480 |
++ # Ensure the file is always owned by the nova user so qemu can't |
481 |
++ # write it. |
482 |
++ utils.chown(self.disk_info_path, owner_uid=os.getuid()) |
483 |
++ |
484 |
++ try: |
485 |
++ if (self.disk_info_path is not None and |
486 |
++ os.path.exists(self.disk_info_path)): |
487 |
++ with open(self.disk_info_path) as disk_info_file: |
488 |
++ line = disk_info_file.read().rstrip() |
489 |
++ dct = _dict_from_line(line) |
490 |
++ for path, driver_format in dct.iteritems(): |
491 |
++ if path == self.path: |
492 |
++ return driver_format |
493 |
++ driver_format = self._get_driver_format() |
494 |
++ if self.disk_info_path is not None: |
495 |
++ fileutils.ensure_tree(os.path.dirname(self.disk_info_path)) |
496 |
++ write_to_disk_info_file() |
497 |
++ except OSError as e: |
498 |
++ raise exception.DiskInfoReadWriteFail(reason=unicode(e)) |
499 |
+ return driver_format |
500 |
+ |
501 |
|
502 |
class Raw(Image): |
503 |
def __init__(self, instance=None, disk_name=None, path=None, |
504 |
-@@ -243,12 +288,17 @@ class Raw(Image): |
505 |
+@@ -243,12 +307,17 @@ def __init__(self, instance=None, disk_name=None, path=None, |
506 |
disk_name)) |
507 |
self.snapshot_name = snapshot_name |
508 |
self.preallocate = CONF.preallocate_images != 'none' |
509 |
@@ -559,7 +717,7 @@ |
510 |
|
511 |
def create_image(self, prepare_template, base, size, *args, **kwargs): |
512 |
@utils.synchronized(base, external=True, lock_path=self.lock_path) |
513 |
-@@ -291,6 +341,9 @@ class Qcow2(Image): |
514 |
+@@ -291,6 +360,9 @@ def __init__(self, instance=None, disk_name=None, path=None, |
515 |
disk_name)) |
516 |
self.snapshot_name = snapshot_name |
517 |
self.preallocate = CONF.preallocate_images != 'none' |
518 |
@@ -570,7 +728,5 @@ |
519 |
def create_image(self, prepare_template, base, size, *args, **kwargs): |
520 |
@utils.synchronized(base, external=True, lock_path=self.lock_path) |
521 |
-- |
522 |
-1.8.5.3 |
523 |
- |
524 |
- |
525 |
+1.8.5.5 |