Skip to content

Commit 9df1103

Browse files
committed
Add a function to re-read size of the loop device
1 parent 515f99b commit 9df1103

5 files changed

Lines changed: 108 additions & 3 deletions

File tree

docs/libblockdev-sections.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ bd_loop_setup
263263
bd_loop_setup_from_fd
264264
bd_loop_teardown
265265
bd_loop_set_autoclear
266+
bd_loop_set_capacity
266267
BDLoopTech
267268
BDLoopTechMode
268269
bd_loop_is_tech_avail

src/lib/plugin_apis/loop.api

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,18 @@ gboolean bd_loop_teardown (const gchar *loop, GError **error);
185185
*/
186186
gboolean bd_loop_set_autoclear (const gchar *loop, gboolean autoclear, GError **error);
187187

188+
/**
189+
* bd_loop_set_capacity:
190+
* @loop: path or name of the loop device
191+
* @error: (out) (optional): place to store error (if any)
192+
*
193+
* Force the loop driver to reread the size of the file associated with the
194+
* specified @loop device.
195+
*
196+
* Returns: whether the LOOP_SET_CAPACITY ioctl was successfully issued or not.
197+
*
198+
* Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_MODIFY
199+
*/
200+
gboolean bd_loop_set_capacity (const gchar *loop, GError **error);
201+
188202
#endif /* BD_LOOP_API */

src/plugins/loop.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,3 +517,64 @@ gboolean bd_loop_set_autoclear (const gchar *loop, gboolean autoclear, GError **
517517
bd_utils_report_finished (progress_id, "Completed");
518518
return TRUE;
519519
}
520+
521+
/**
522+
* bd_loop_set_capacity:
523+
* @loop: path or name of the loop device
524+
* @error: (out) (optional): place to store error (if any)
525+
*
526+
* Force the loop driver to reread the size of the file associated with the
527+
* specified @loop device.
528+
*
529+
* Returns: whether the LOOP_SET_CAPACITY ioctl was successfully issued or not.
530+
*
531+
* Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_MODIFY
532+
*/
533+
gboolean bd_loop_set_capacity (const gchar *loop, GError **error) {
534+
gchar *dev_loop = NULL;
535+
gint fd = -1;
536+
guint64 progress_id = 0;
537+
gchar *msg = NULL;
538+
guint n_try = 0;
539+
gint status = 0;
540+
GError *l_error = NULL;
541+
542+
if (!g_str_has_prefix (loop, "/dev/"))
543+
dev_loop = g_strdup_printf ("/dev/%s", loop);
544+
545+
msg = g_strdup_printf ("Started setting up capacity on the /dev/%s device",
546+
dev_loop ? dev_loop : loop);
547+
progress_id = bd_utils_report_started (msg);
548+
g_free (msg);
549+
550+
fd = open (dev_loop ? dev_loop : loop, O_RDWR);
551+
g_free (dev_loop);
552+
if (fd < 0) {
553+
g_set_error (&l_error, BD_LOOP_ERROR, BD_LOOP_ERROR_DEVICE,
554+
"Failed to open device %s: %m", loop);
555+
bd_utils_report_finished (progress_id, l_error->message);
556+
g_propagate_error (error, l_error);
557+
return FALSE;
558+
}
559+
560+
for (n_try=10, status=-1; (status != 0) && (n_try > 0); n_try--) {
561+
status = ioctl (fd, LOOP_SET_CAPACITY, 0);
562+
if (status < 0 && errno == EAGAIN)
563+
g_usleep (100 * 1000); /* microseconds */
564+
else
565+
break;
566+
}
567+
568+
if (status != 0) {
569+
g_set_error (&l_error, BD_LOOP_ERROR, BD_LOOP_ERROR_FAIL,
570+
"Failed to set capacity of the device %s: %m", loop);
571+
close (fd);
572+
bd_utils_report_finished (progress_id, l_error->message);
573+
g_propagate_error (error, l_error);
574+
return FALSE;
575+
}
576+
577+
close (fd);
578+
bd_utils_report_finished (progress_id, "Completed");
579+
return TRUE;
580+
}

src/plugins/loop.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,6 @@ gboolean bd_loop_setup_from_fd (gint fd, guint64 offset, guint64 size, gboolean
6666
gboolean bd_loop_teardown (const gchar *loop, GError **error);
6767

6868
gboolean bd_loop_set_autoclear (const gchar *loop, gboolean autoclear, GError **error);
69+
gboolean bd_loop_set_capacity (const gchar *loop, GError **error);
6970

7071
#endif /* BD_LOOP */

tests/loop_test.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import unittest
33
import overrides_hack
44

5-
from utils import create_sparse_tempfile, TestTags, tag_test, required_plugins
5+
from utils import create_sparse_tempfile, create_sparse_file, TestTags, tag_test, required_plugins
66

77
import gi
88
gi.require_version('BlockDev', '3.0')
@@ -11,6 +11,7 @@
1111

1212
@required_plugins(("loop",))
1313
class LoopTestCase(unittest.TestCase):
14+
_loop_size = 100 * 1024**2
1415

1516
requested_plugins = BlockDev.plugin_specs_from_names(("loop",))
1617

@@ -23,7 +24,7 @@ def setUpClass(cls):
2324

2425
def setUp(self):
2526
self.addCleanup(self._clean_up)
26-
self.dev_file = create_sparse_tempfile("loop_test", 1024**3)
27+
self.dev_file = create_sparse_tempfile("loop_test", self._loop_size)
2728
self.loop = None
2829

2930
def _clean_up(self):
@@ -77,7 +78,7 @@ def test_loop_setup_with_offset(self):
7778
# should have smaller size due to the offset
7879
with open("/sys/block/%s/size" % self.loop, "r") as f:
7980
size = int(f.read()) * 512
80-
self.assertEqual(size, 1024**3 - 10 * 1024 **2)
81+
self.assertEqual(size, self._loop_size - 10 * 1024 **2)
8182

8283
succ = BlockDev.loop_teardown(self.loop)
8384
self.assertTrue(succ)
@@ -219,3 +220,30 @@ def test_loop_get_set_autoclear(self):
219220
info = BlockDev.loop_info(self.loop)
220221
self.assertIsNotNone(info)
221222
self.assertFalse(info.autoclear)
223+
224+
225+
class LoopTestSetCapacity(LoopTestCase):
226+
def test_loop_set_capacity(self):
227+
succ, self.loop = BlockDev.loop_setup(self.dev_file)
228+
self.assertTrue(succ)
229+
self.assertTrue(self.loop)
230+
231+
with open("/sys/block/%s/size" % self.loop, "r") as f:
232+
size = int(f.read()) * 512
233+
self.assertEqual(size, self._loop_size)
234+
235+
# enlarge the backing file
236+
create_sparse_file(self.dev_file, self._loop_size * 2)
237+
238+
# size shouldn't change without forcing re-read
239+
with open("/sys/block/%s/size" % self.loop, "r") as f:
240+
size = int(f.read()) * 512
241+
self.assertEqual(size, self._loop_size)
242+
243+
succ = BlockDev.loop_set_capacity(self.loop)
244+
self.assertTrue(succ)
245+
246+
# now the size should be updated
247+
with open("/sys/block/%s/size" % self.loop, "r") as f:
248+
size = int(f.read()) * 512
249+
self.assertEqual(size, self._loop_size * 2)

0 commit comments

Comments
 (0)