Codebase list libblockdev / 5b49492
New upstream version 2.25 Michael Biebl 3 years ago
21 changed file(s) with 599 addition(s) and 363 deletion(s). Raw diff Collapse all Expand all
0 Libblockdev 2.25
1 ----------------
2
3 New minor release of the libblockdev library with multiple fixes. See below
4 for details.
5
6 **Full list of changes**
7
8 Tomas Bzatek (6):
9 - exec: Fix polling for stdout and stderr
10 - exec: Use non-blocking read and process the buffer manually
11 - exec: Clarify the BDUtilsProgExtract callback documentation
12 - tests: Add bufferbloat exec tests
13 - tests: Add null-byte exec tests
14 - lvm: Fix bd_lvm_vdopooldata_* symbols
15
16 Vojtech Trefny (10):
17 - exec: Fix setting locale for util calls
18 - fs: Do not report error when errors were fixed by e2fsck
19 - README: Use CI status image for 2.x-branch on 2.x
20 - fs: Fix compile error in ext_repair caused by cherry pick from master
21 - Mark all GIR file constants as guint64
22 - lvm: Set thin metadata limits to match limits LVM uses in lvcreate
23 - lvm: Do not use thin_metadata_size to recommend thin metadata size
24 - lvm: Use the UNUSED macro instead of __attribute__((unused))
25 - Fix max size limit for LVM thinpool metadata
26 - loop: Retry LOOP_SET_STATUS64 on EAGAIN
27
28
029 Libblockdev 2.24
130 ----------------
231
00 ### CI status
11
2 <img alt="CI status" src="https://fedorapeople.org/groups/storage_apis/statuses/libblockdev-master.svg" width="100%" height="300ex" />
2 <img alt="CI status" src="https://fedorapeople.org/groups/storage_apis/statuses/libblockdev-2.x.svg" width="100%" height="300ex" />
33
44
55 ### Introduction
00 # configure.ac for libblockdev
11
2 AC_INIT([libblockdev], [2.24], [vpodzime@redhat.com])
2 AC_INIT([libblockdev], [2.25], [vpodzime@redhat.com])
33
44 # Disable building static libraries.
55 # This needs to be set before initializing automake
123123 %define configure_opts %{?python2_copts} %{?python3_copts} %{?bcache_copts} %{?lvm_dbus_copts} %{?btrfs_copts} %{?crypto_copts} %{?dm_copts} %{?loop_copts} %{?lvm_copts} %{?lvm_dbus_copts} %{?mdraid_copts} %{?mpath_copts} %{?swap_copts} %{?kbd_copts} %{?part_copts} %{?fs_copts} %{?nvdimm_copts} %{?vdo_copts} %{?tools_copts} %{?gi_copts}
124124
125125 Name: libblockdev
126 Version: 2.24
126 Version: 2.25
127127 Release: 1%{?dist}
128128 Summary: A library for low-level manipulation with block devices
129129 License: LGPLv2+
386386 Summary: The LVM plugin for the libblockdev library
387387 Requires: %{name}-utils%{?_isa} >= 0.11
388388 Requires: lvm2
389 # for thin_metadata_size
390 Requires: device-mapper-persistent-data
391389
392390 %description lvm
393391 The libblockdev library plugin (and in the same time a standalone library)
410408 Summary: The LVM plugin for the libblockdev library
411409 Requires: %{name}-utils%{?_isa} >= 1.4
412410 Requires: lvm2-dbusd >= 2.02.156
413 # for thin_metadata_size
414 Requires: device-mapper-persistent-data
415411
416412 %description lvm-dbus
417413 The libblockdev library plugin (and in the same time a standalone library)
982978 %files plugins-all
983979
984980 %changelog
981 * Mon Jan 11 2021 Vojtech Trefny <vtrefny@redhat.com> - 2.25-1
982 - loop: Retry LOOP_SET_STATUS64 on EAGAIN (vtrefny)
983 - Fix max size limit for LVM thinpool metadata (vtrefny)
984 - lvm: Use the UNUSED macro instead of __attribute__((unused)) (vtrefny)
985 - lvm: Do not use thin_metadata_size to recommend thin metadata size (vtrefny)
986 - lvm: Set thin metadata limits to match limits LVM uses in lvcreate (vtrefny)
987 - Mark all GIR file constants as guint64 (vtrefny)
988 - lvm: Fix bd_lvm_vdopooldata_* symbols (tbzatek)
989 - fs: Fix compile error in ext_repair caused by cherry pick from master (vtrefny)
990 - README: Use CI status image for 2.x-branch on 2.x (vtrefny)
991 - fs: Do not report error when errors were fixed by e2fsck (vtrefny)
992 - tests: Add null-byte exec tests (tbzatek)
993 - tests: Add bufferbloat exec tests (tbzatek)
994 - exec: Clarify the BDUtilsProgExtract callback documentation (tbzatek)
995 - exec: Use non-blocking read and process the buffer manually (tbzatek)
996 - exec: Fix polling for stdout and stderr (tbzatek)
997 - exec: Fix setting locale for util calls (vtrefny)
998
985999 * Fri May 22 2020 Vojtech Trefny <vtrefny@redhat.com> - 2.24-1
9861000 - Mark VDO plugin as deprecated since 2.24 (vtrefny)
9871001 - Fix multiple uninitialized values discovered by coverity (vtrefny)
22 #include <blockdev/utils.h>
33
44 #define BD_BTRFS_MAIN_VOLUME_ID 5
5 #define BD_BTRFS_MIN_MEMBER_SIZE (128 MiB)
5 #define BD_BTRFS_MIN_MEMBER_SIZE G_GUINT64_CONSTANT (134217728ULL) // 128 MiB
66
77 GQuark bd_btrfs_error_quark (void) {
88 return g_quark_from_static_string ("g-bd-btrfs-error-quark");
00 #include <glib.h>
11 #include <blockdev/utils.h>
22
3 #define BD_CRYPTO_LUKS_METADATA_SIZE (2 MiB)
3 #define BD_CRYPTO_LUKS_METADATA_SIZE G_GUINT64_CONSTANT (2097152ULL) // 2 MiB
44
55 GQuark bd_crypto_error_quark (void) {
66 return g_quark_from_static_string ("g-bd-crypto-error-quark");
1414 #define BD_LVM_MAX_LV_SIZE G_GUINT64_CONSTANT (9223372036854775808ULL)
1515
1616
17 #define BD_LVM_DEFAULT_PE_START (1 MiB)
18 #define BD_LVM_DEFAULT_PE_SIZE (4 MiB)
19 #define BD_LVM_MIN_PE_SIZE (1 KiB)
20 #define BD_LVM_MAX_PE_SIZE (16 GiB)
21 #define BD_LVM_MIN_THPOOL_MD_SIZE (2 MiB)
22 #define BD_LVM_MAX_THPOOL_MD_SIZE (16 GiB)
23 #define BD_LVM_MIN_THPOOL_CHUNK_SIZE (64 KiB)
24 #define BD_LVM_MAX_THPOOL_CHUNK_SIZE (1 GiB)
25 #define BD_LVM_DEFAULT_CHUNK_SIZE (64 KiB)
17 #define BD_LVM_DEFAULT_PE_START G_GUINT64_CONSTANT (1048576ULL) // 1 MiB
18 #define BD_LVM_DEFAULT_PE_SIZE G_GUINT64_CONSTANT (4194304ULL) // 4 MiB
19 #define BD_LVM_MIN_PE_SIZE G_GUINT64_CONSTANT (1024ULL) // 1 KiB
20 #define BD_LVM_MAX_PE_SIZE G_GUINT64_CONSTANT (17179869184ULL) // 16 GiB
21 #define BD_LVM_MIN_THPOOL_MD_SIZE G_GUINT64_CONSTANT (4194304ULL) // 4 MiB
22 #define BD_LVM_MAX_THPOOL_MD_SIZE G_GUINT64_CONSTANT (16978542592ULL) // 15.81 GiB
23 #define BD_LVM_MIN_THPOOL_CHUNK_SIZE G_GUINT64_CONSTANT (65536ULL) // 64 KiB
24 #define BD_LVM_MAX_THPOOL_CHUNK_SIZE G_GUINT64_CONSTANT (1073741824ULL) // 1 GiB
25 #define BD_LVM_DEFAULT_CHUNK_SIZE G_GUINT64_CONSTANT (65536ULL) // 64 KiB
2626
2727 /* according to lvmcache (7) */
28 #define BD_LVM_MIN_CACHE_MD_SIZE (8 MiB)
28 #define BD_LVM_MIN_CACHE_MD_SIZE (8388608ULL) // 8 MiB
2929
3030 GQuark bd_lvm_error_quark (void) {
3131 return g_quark_from_static_string ("g-bd-lvm-error-quark");
359359 return type;
360360 }
361361
362 #define BD_LVM_TYPE_VDODATA (bd_lvm_vdodata_get_type ())
363 GType bd_lvm_vdodata_get_type();
362 #define BD_LVM_TYPE_VDOPOOLDATA (bd_lvm_vdopooldata_get_type ())
363 GType bd_lvm_vdopooldata_get_type();
364364
365365 /**
366366 * BDLVMVDOPooldata:
387387 } BDLVMVDOPooldata;
388388
389389 /**
390 * bd_lvm_vdodata_copy: (skip)
390 * bd_lvm_vdopooldata_copy: (skip)
391391 *
392392 * Creates a new copy of @data.
393393 */
394 BDLVMVDOPooldata* bd_lvm_vdodata_copy (BDLVMVDOPooldata *data) {
394 BDLVMVDOPooldata* bd_lvm_vdopooldata_copy (BDLVMVDOPooldata *data) {
395395 if (data == NULL)
396396 return NULL;
397397
410410 }
411411
412412 /**
413 * bd_lvm_vdodata_free: (skip)
413 * bd_lvm_vdopooldata_free: (skip)
414414 *
415415 * Frees @data.
416416 */
417 void bd_lvm_vdodata_free (BDLVMVDOPooldata *data) {
417 void bd_lvm_vdopooldata_free (BDLVMVDOPooldata *data) {
418418 if (data == NULL)
419419 return;
420420
421421 g_free (data);
422422 }
423423
424 GType bd_lvm_vdodata_get_type () {
424 GType bd_lvm_vdopooldata_get_type () {
425425 static GType type = 0;
426426
427427 if (G_UNLIKELY(type == 0)) {
428428 type = g_boxed_type_register_static("BDLVMVDOPooldata",
429 (GBoxedCopyFunc) bd_lvm_vdodata_copy,
430 (GBoxedFreeFunc) bd_lvm_vdodata_free);
429 (GBoxedCopyFunc) bd_lvm_vdopooldata_copy,
430 (GBoxedFreeFunc) bd_lvm_vdopooldata_free);
431431 }
432432
433433 return type;
703703 * bd_lvm_get_thpool_meta_size:
704704 * @size: size of the thin pool
705705 * @chunk_size: chunk size of the thin pool or 0 to use the default (%BD_LVM_DEFAULT_CHUNK_SIZE)
706 * @n_snapshots: number of snapshots that will be created in the pool
707 * @error: (out): place to store error (if any)
708 *
709 * Returns: recommended size of the metadata space for the specified pool or 0
710 * in case of error
706 * @n_snapshots: ignored
707 * @error: (out): place to store error (if any)
708 *
709 * Note: This function will be changed in 3.0: the @n_snapshots parameter
710 * is currently not used and will be removed.
711 *
712 * Returns: recommended size of the metadata space for the specified pool
711713 *
712714 * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
713715 */
66
77 /* taken from blivet */
88 // these defaults were determined empirically
9 #define BD_MD_SUPERBLOCK_SIZE (2 MiB)
10 #define BD_MD_CHUNK_SIZE (512 KiB)
9 #define BD_MD_SUPERBLOCK_SIZE G_GUINT64_CONSTANT (2097152ULL) // 2 MiB
10 #define BD_MD_CHUNK_SIZE G_GUINT64_CONSTANT (524288ULL) // 512 KiB
1111
1212 GQuark bd_md_error_quark (void) {
1313 return g_quark_from_static_string ("g-bd-md-error-quark");
403403 const gchar *args_progress[7] = {"e2fsck", "-f", unsafe ? "-y" : "-p", "-C", "1", device, NULL};
404404 const gchar *args[5] = {"e2fsck", "-f", unsafe ? "-y" : "-p", device, NULL};
405405 gint status = 0;
406 gboolean ret = FALSE;
406407
407408 if (!check_deps (&avail_deps, DEPS_E2FSCK_MASK, deps, DEPS_LAST, &deps_check_lock, error))
408409 return FALSE;
409410
410411 if (bd_utils_prog_reporting_initialized ()) {
411 return bd_utils_exec_and_report_progress (args_progress, extra, extract_e2fsck_progress, &status, error);
412 ret = bd_utils_exec_and_report_progress (args_progress, extra, extract_e2fsck_progress, &status, error);
412413 } else {
413 return bd_utils_exec_and_report_status_error (args, extra, &status, error);
414 }
414 ret = bd_utils_exec_and_report_status_error (args, extra, &status, error);
415 }
416
417 if (!ret) {
418 if (status == 1) {
419 /* no error should be reported for exit code 1 - File system errors corrected */
420 g_clear_error (error);
421 ret = TRUE;
422 } else if (status == 2) {
423 /* no error should be reported for exit code 2 - File system errors corrected, system should be rebooted */
424 g_warning ("File system errors on %s were successfully corrected, but system reboot is advised.", device);
425 g_clear_error (error);
426 ret = TRUE;
427 }
428 }
429 return ret;
415430 }
416431
417432 /**
2323 #include <fcntl.h>
2424 #include <sys/ioctl.h>
2525 #include <linux/loop.h>
26 #include <errno.h>
2627 #include <blockdev/utils.h>
2728 #include "loop.h"
2829
231232 gint loop_fd = -1;
232233 struct loop_info64 li64;
233234 guint64 progress_id = 0;
235 gint status = 0;
236 guint n_try = 0;
234237
235238 progress_id = bd_utils_report_started ("Started setting up loop device");
236239
287290
288291 bd_utils_report_progress (progress_id, 66, "Associated the loop device");
289292
290 if (ioctl (loop_fd, LOOP_SET_STATUS64, &li64) < 0) {
293 /* we may need to try multiple times with some delays in case the device is
294 busy at the very moment */
295 for (n_try=10, status=-1; (status != 0) && (n_try > 0); n_try--) {
296 status = ioctl (loop_fd, LOOP_SET_STATUS64, &li64);
297 if (status < 0 && errno == EAGAIN)
298 g_usleep (100 * 1000); /* microseconds */
299 else
300 break;
301 }
302
303 if (status != 0) {
291304 g_set_error (error, BD_LOOP_ERROR, BD_LOOP_ERROR_FAIL,
292305 "Failed to set status for the %s device: %m", loop_device);
293306 g_free (loop_device);
1919 #include <glib.h>
2020 #include <math.h>
2121 #include <string.h>
22 #include <libdevmapper.h>
2322 #include <unistd.h>
2423 #include <blockdev/utils.h>
2524 #include <gio/gio.h>
247246 static volatile guint avail_module_deps = 0;
248247 static GMutex deps_check_lock;
249248
250 #define DEPS_THMS 0
251 #define DEPS_THMS_MASK (1 << DEPS_THMS)
252 #define DEPS_LAST 1
253
254 static const UtilDep deps[DEPS_LAST] = {
255 {"thin_metadata_size", NULL, NULL, NULL},
256 };
257
258249 #define DBUS_DEPS_LVMDBUSD 0
259250 #define DBUS_DEPS_LVMDBUSD_MASK (1 << DBUS_DEPS_LVMDBUSD)
260251 #define DBUS_DEPS_LAST 1
300291 check_ret = check_ret && success;
301292 }
302293
303 for (i=0; i < DEPS_LAST; i++) {
304 success = bd_utils_check_util_version (deps[i].name, deps[i].version,
305 deps[i].ver_arg, deps[i].ver_regexp, &error);
306 if (!success)
307 g_warning ("%s", error->message);
308 else
309 g_atomic_int_or (&avail_deps, 1 << i);
310 g_clear_error (&error);
311 check_ret = check_ret && success;
312 }
313
314294 if (!check_ret)
315295 g_warning("Cannot load the LVM plugin");
316296
385365 g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
386366 "Only 'query' supported for thin calculations");
387367 return FALSE;
388 } else if ((mode & BD_LVM_TECH_MODE_QUERY) &&
389 !check_deps (&avail_deps, DEPS_THMS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
390 return FALSE;
391 else
368 } else
392369 return TRUE;
393370 case BD_LVM_TECH_CALCS:
394371 if (mode & ~BD_LVM_TECH_MODE_QUERY) {
981958 return data;
982959 }
983960
984 static BDLVMVGdata* get_vg_data_from_props (GVariant *props, GError **error __attribute__((unused))) {
961 static BDLVMVGdata* get_vg_data_from_props (GVariant *props, GError **error UNUSED) {
985962 BDLVMVGdata *data = g_new0 (BDLVMVGdata, 1);
986963 GVariantDict dict;
987964
10831060 return data;
10841061 }
10851062
1086 static BDLVMVDOPooldata* get_vdo_data_from_props (GVariant *props, GError **error __attribute__((unused))) {
1063 static BDLVMVDOPooldata* get_vdo_data_from_props (GVariant *props, GError **error UNUSED) {
10871064 BDLVMVDOPooldata *data = g_new0 (BDLVMVDOPooldata, 1);
10881065 GVariantDict dict;
10891066 gchar *value = NULL;
11871164 *
11881165 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
11891166 */
1190 gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error __attribute__((unused))) {
1167 gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error UNUSED) {
11911168 return (((size % 2) == 0) && (size >= (BD_LVM_MIN_PE_SIZE)) && (size <= (BD_LVM_MAX_PE_SIZE)));
11921169 }
11931170
11991176 *
12001177 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
12011178 */
1202 guint64 *bd_lvm_get_supported_pe_sizes (GError **error __attribute__((unused))) {
1179 guint64 *bd_lvm_get_supported_pe_sizes (GError **error UNUSED) {
12031180 guint8 i;
12041181 guint64 val = BD_LVM_MIN_PE_SIZE;
12051182 guint8 num_items = ((guint8) round (log2 ((double) BD_LVM_MAX_PE_SIZE))) - ((guint8) round (log2 ((double) BD_LVM_MIN_PE_SIZE))) + 2;
12211198 *
12221199 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
12231200 */
1224 guint64 bd_lvm_get_max_lv_size (GError **error __attribute__((unused))) {
1201 guint64 bd_lvm_get_max_lv_size (GError **error UNUSED) {
12251202 return BD_LVM_MAX_LV_SIZE;
12261203 }
12271204
12411218 *
12421219 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
12431220 */
1244 guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error __attribute__((unused))) {
1221 guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error UNUSED) {
12451222 pe_size = RESOLVE_PE_SIZE(pe_size);
12461223 guint64 delta = size % pe_size;
12471224 if (delta == 0)
13021279 * bd_lvm_get_thpool_meta_size:
13031280 * @size: size of the thin pool
13041281 * @chunk_size: chunk size of the thin pool or 0 to use the default (%BD_LVM_DEFAULT_CHUNK_SIZE)
1305 * @n_snapshots: number of snapshots that will be created in the pool
1306 * @error: (out): place to store error (if any)
1307 *
1308 * Returns: recommended size of the metadata space for the specified pool or 0
1309 * in case of error
1282 * @n_snapshots: ignored
1283 * @error: (out): place to store error (if any)
1284 *
1285 * Note: This function will be changed in 3.0: the @n_snapshots parameter
1286 * is currently not used and will be removed.
1287 *
1288 * Returns: recommended size of the metadata space for the specified pool
13101289 *
13111290 * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
13121291 */
1313 guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots, GError **error) {
1314 /* ub - output in bytes, n - output just the number */
1315 const gchar* args[7] = {"thin_metadata_size", "-ub", "-n", NULL, NULL, NULL, NULL};
1316 gchar *output = NULL;
1317 gboolean success = FALSE;
1318 guint64 ret = 0;
1319
1320 if (!check_deps (&avail_deps, DEPS_THMS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
1321 return 0;
1322
1323 /* s - total size, b - chunk size, m - number of snapshots */
1324 args[3] = g_strdup_printf ("-s%"G_GUINT64_FORMAT, size);
1325 args[4] = g_strdup_printf ("-b%"G_GUINT64_FORMAT,
1326 chunk_size != 0 ? chunk_size : (guint64) BD_LVM_DEFAULT_CHUNK_SIZE);
1327 args[5] = g_strdup_printf ("-m%"G_GUINT64_FORMAT, n_snapshots);
1328
1329 success = bd_utils_exec_and_capture_output (args, NULL, &output, error);
1330 g_free ((gchar*) args[3]);
1331 g_free ((gchar*) args[4]);
1332 g_free ((gchar*) args[5]);
1333
1334 if (!success) {
1335 /* error is already set */
1336 g_free (output);
1337 return 0;
1338 }
1339
1340 ret = g_ascii_strtoull (output, NULL, 0);
1341 if (ret == 0) {
1342 g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
1343 "Failed to parse number from thin_metadata_size's output: '%s'",
1344 output);
1345 g_free (output);
1346 return 0;
1347 }
1348
1349 g_free (output);
1350
1351 return MAX (ret, BD_LVM_MIN_THPOOL_MD_SIZE);
1292 guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots UNUSED, GError **error UNUSED) {
1293 guint64 md_size = 0;
1294
1295 /* based on lvcreate metadata size calculation */
1296 md_size = UINT64_C(64) * size / (chunk_size ? chunk_size : BD_LVM_DEFAULT_CHUNK_SIZE);
1297
1298 if (md_size > BD_LVM_MAX_THPOOL_MD_SIZE)
1299 md_size = BD_LVM_MAX_THPOOL_MD_SIZE;
1300 else if (md_size < BD_LVM_MIN_THPOOL_MD_SIZE)
1301 md_size = BD_LVM_MIN_THPOOL_MD_SIZE;
1302
1303 return md_size;
13521304 }
13531305
13541306 /**
13601312 *
13611313 * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
13621314 */
1363 gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error __attribute__((unused))) {
1315 gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error UNUSED) {
13641316 return ((BD_LVM_MIN_THPOOL_MD_SIZE <= size) && (size <= BD_LVM_MAX_THPOOL_MD_SIZE));
13651317 }
13661318
13741326 *
13751327 * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
13761328 */
1377 gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error __attribute__((unused))) {
1329 gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error UNUSED) {
13781330 gdouble size_log2 = 0.0;
13791331
13801332 if ((size < BD_LVM_MIN_THPOOL_CHUNK_SIZE) || (size > BD_LVM_MAX_THPOOL_CHUNK_SIZE))
26632615 *
26642616 * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
26652617 */
2666 gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error __attribute__((unused))) {
2618 gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error UNUSED) {
26672619 /* XXX: the error attribute will likely be used in the future when
26682620 some validation comes into the game */
26692621
26882640 *
26892641 * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
26902642 */
2691 gchar* bd_lvm_get_global_config (GError **error __attribute__((unused))) {
2643 gchar* bd_lvm_get_global_config (GError **error UNUSED) {
26922644 gchar *ret = NULL;
26932645
26942646 g_mutex_lock (&global_config_lock);
27072659 *
27082660 * Tech category: %BD_LVM_TECH_CACHE_CALCS no mode (it is ignored)
27092661 */
2710 guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error __attribute__((unused))) {
2662 guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error UNUSED) {
27112663 return MAX ((guint64) cache_size / 1000, BD_LVM_MIN_CACHE_MD_SIZE);
27122664 }
27132665
27172669 *
27182670 * Get LV type string from flags.
27192671 */
2720 static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error __attribute__((unused))) {
2672 static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error UNUSED) {
27212673 if (!meta) {
27222674 if (flags & BD_LVM_CACHE_POOL_STRIPED)
27232675 return "striped";
1919 #include <glib.h>
2020 #include <math.h>
2121 #include <string.h>
22 #include <libdevmapper.h>
2322 #include <unistd.h>
2423 #include <blockdev/utils.h>
2524
154153 g_free (data);
155154 }
156155
157 BDLVMVDOPooldata* bd_lvm_vdodata_copy (BDLVMVDOPooldata *data) {
156 BDLVMVDOPooldata* bd_lvm_vdopooldata_copy (BDLVMVDOPooldata *data) {
158157 if (data == NULL)
159158 return NULL;
160159
163162 new_data->operating_mode = data->operating_mode;
164163 new_data->compression_state = data->compression_state;
165164 new_data->index_state = data->index_state;
165 new_data->write_policy = data->write_policy;
166166 new_data->used_size = data->used_size;
167167 new_data->saving_percent = data->saving_percent;
168 new_data->index_memory_size = data->index_memory_size;
168169 new_data->deduplication = data->deduplication;
169170 new_data->compression = data->compression;
170171 return new_data;
171172 }
172173
173 void bd_lvm_vdodata_free (BDLVMVDOPooldata *data) {
174 void bd_lvm_vdopooldata_free (BDLVMVDOPooldata *data) {
174175 if (data == NULL)
175176 return;
176177
210211
211212 #define DEPS_LVM 0
212213 #define DEPS_LVM_MASK (1 << DEPS_LVM)
213 #define DEPS_THMS 1
214 #define DEPS_THMS_MASK (1 << DEPS_THMS)
215 #define DEPS_LAST 2
214 #define DEPS_LAST 1
216215
217216 static const UtilDep deps[DEPS_LAST] = {
218217 {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"},
219 {"thin_metadata_size", NULL, NULL, NULL},
220218 };
221219
222220 #define FEATURES_VDO 0
232230 #define MODULE_DEPS_LAST 1
233231
234232 static const gchar*const module_deps[MODULE_DEPS_LAST] = { "kvdo" };
233
234 #define UNUSED __attribute__((unused))
235235
236236 /**
237237 * bd_lvm_check_deps:
314314 g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
315315 "Only 'query' supported for thin calculations");
316316 return FALSE;
317 } else if ((mode & BD_LVM_TECH_MODE_QUERY) &&
318 !check_deps (&avail_deps, DEPS_THMS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
319 return FALSE;
320 else
317 } else
321318 return TRUE;
322319 case BD_LVM_TECH_CALCS:
323320 if (mode & ~BD_LVM_TECH_MODE_QUERY) {
702699 *
703700 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
704701 */
705 gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error __attribute__((unused))) {
702 gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error UNUSED) {
706703 return (((size % 2) == 0) && (size >= (BD_LVM_MIN_PE_SIZE)) && (size <= (BD_LVM_MAX_PE_SIZE)));
707704 }
708705
714711 *
715712 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
716713 */
717 guint64 *bd_lvm_get_supported_pe_sizes (GError **error __attribute__((unused))) {
714 guint64 *bd_lvm_get_supported_pe_sizes (GError **error UNUSED) {
718715 guint8 i;
719716 guint64 val = BD_LVM_MIN_PE_SIZE;
720717 guint8 num_items = ((guint8) round (log2 ((double) BD_LVM_MAX_PE_SIZE))) - ((guint8) round (log2 ((double) BD_LVM_MIN_PE_SIZE))) + 2;
736733 *
737734 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
738735 */
739 guint64 bd_lvm_get_max_lv_size (GError **error __attribute__((unused))) {
736 guint64 bd_lvm_get_max_lv_size (GError **error UNUSED) {
740737 return BD_LVM_MAX_LV_SIZE;
741738 }
742739
756753 *
757754 * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
758755 */
759 guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error __attribute__((unused))) {
756 guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error UNUSED) {
760757 pe_size = RESOLVE_PE_SIZE(pe_size);
761758 guint64 delta = size % pe_size;
762759 if (delta == 0)
817814 * bd_lvm_get_thpool_meta_size:
818815 * @size: size of the thin pool
819816 * @chunk_size: chunk size of the thin pool or 0 to use the default (%BD_LVM_DEFAULT_CHUNK_SIZE)
820 * @n_snapshots: number of snapshots that will be created in the pool
821 * @error: (out): place to store error (if any)
822 *
823 * Returns: recommended size of the metadata space for the specified pool or 0
824 * in case of error
817 * @n_snapshots: ignored
818 * @error: (out): place to store error (if any)
819 *
820 * Note: This function will be changed in 3.0: the @n_snapshots parameter
821 * is currently not used and will be removed.
822 *
823 * Returns: recommended size of the metadata space for the specified pool
825824 *
826825 * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
827826 */
828 guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots, GError **error) {
829 /* ub - output in bytes, n - output just the number */
830 const gchar* args[7] = {"thin_metadata_size", "-ub", "-n", NULL, NULL, NULL, NULL};
831 gchar *output = NULL;
832 gboolean success = FALSE;
833 guint64 ret = 0;
834
835 if (!check_deps (&avail_deps, DEPS_THMS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
836 return 0;
837
838 /* s - total size, b - chunk size, m - number of snapshots */
839 args[3] = g_strdup_printf ("-s%"G_GUINT64_FORMAT, size);
840 args[4] = g_strdup_printf ("-b%"G_GUINT64_FORMAT,
841 chunk_size != 0 ? chunk_size : (guint64) BD_LVM_DEFAULT_CHUNK_SIZE);
842 args[5] = g_strdup_printf ("-m%"G_GUINT64_FORMAT, n_snapshots);
843
844 success = bd_utils_exec_and_capture_output (args, NULL, &output, error);
845 g_free ((gchar*) args[3]);
846 g_free ((gchar*) args[4]);
847 g_free ((gchar*) args[5]);
848
849 if (!success) {
850 /* error is already set */
851 g_free (output);
852 return 0;
853 }
854
855 ret = g_ascii_strtoull (output, NULL, 0);
856 if (ret == 0) {
857 g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
858 "Failed to parse number from thin_metadata_size's output: '%s'",
859 output);
860 g_free (output);
861 return 0;
862 }
863
864 g_free (output);
865
866 return MAX (ret, BD_LVM_MIN_THPOOL_MD_SIZE);
827 guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots UNUSED, GError **error UNUSED) {
828 guint64 md_size = 0;
829
830 /* based on lvcreate metadata size calculation */
831 md_size = UINT64_C(64) * size / (chunk_size ? chunk_size : BD_LVM_DEFAULT_CHUNK_SIZE);
832
833 if (md_size > BD_LVM_MAX_THPOOL_MD_SIZE)
834 md_size = BD_LVM_MAX_THPOOL_MD_SIZE;
835 else if (md_size < BD_LVM_MIN_THPOOL_MD_SIZE)
836 md_size = BD_LVM_MIN_THPOOL_MD_SIZE;
837
838 return md_size;
867839 }
868840
869841 /**
875847 *
876848 * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
877849 */
878 gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error __attribute__((unused))) {
850 gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error UNUSED) {
879851 return ((BD_LVM_MIN_THPOOL_MD_SIZE <= size) && (size <= BD_LVM_MAX_THPOOL_MD_SIZE));
880852 }
881853
889861 *
890862 * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
891863 */
892 gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error __attribute__((unused))) {
864 gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error UNUSED) {
893865 gdouble size_log2 = 0.0;
894866
895867 if ((size < BD_LVM_MIN_THPOOL_CHUNK_SIZE) || (size > BD_LVM_MAX_THPOOL_CHUNK_SIZE))
20101982 *
20111983 * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
20121984 */
2013 gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error __attribute__((unused))) {
1985 gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error UNUSED) {
20141986 /* XXX: the error attribute will likely be used in the future when
20151987 some validation comes into the game */
20161988
20352007 *
20362008 * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
20372009 */
2038 gchar* bd_lvm_get_global_config (GError **error __attribute__((unused))) {
2010 gchar* bd_lvm_get_global_config (GError **error UNUSED) {
20392011 gchar *ret = NULL;
20402012
20412013 g_mutex_lock (&global_config_lock);
20542026 *
20552027 * Tech category: %BD_LVM_TECH_CACHE_CALCS no mode (it is ignored)
20562028 */
2057 guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error __attribute__((unused))) {
2029 guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error UNUSED) {
20582030 return MAX ((guint64) cache_size / 1000, BD_LVM_MIN_CACHE_MD_SIZE);
20592031 }
20602032
20642036 *
20652037 * Get LV type string from flags.
20662038 */
2067 static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error __attribute__((unused))) {
2039 static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error UNUSED) {
20682040 if (!meta) {
20692041 if (flags & BD_LVM_CACHE_POOL_STRIPED)
20702042 return "striped";
00 #include <glib.h>
11 #include <blockdev/utils.h>
2 #include <libdevmapper.h>
23
34 #ifndef BD_LVM
45 #define BD_LVM
2021 #define USE_DEFAULT_PE_SIZE 0
2122 #define RESOLVE_PE_SIZE(size) ((size) == USE_DEFAULT_PE_SIZE ? BD_LVM_DEFAULT_PE_SIZE : (size))
2223
23 #define BD_LVM_MIN_THPOOL_MD_SIZE (2 MiB)
24 #define BD_LVM_MAX_THPOOL_MD_SIZE (16 GiB)
24 /* lvm constant for thin pool metadata size is actually half of this
25 but when they calculate the actual metadata size they double the limit
26 so lets just double the limit here too */
27 #define BD_LVM_MIN_THPOOL_MD_SIZE (4 MiB)
28
29 /* DM_THIN_MAX_METADATA_SIZE is in 512 sectors */
30 #define BD_LVM_MAX_THPOOL_MD_SIZE (DM_THIN_MAX_METADATA_SIZE * 512)
31
2532 #define BD_LVM_MIN_THPOOL_CHUNK_SIZE (64 KiB)
2633 #define BD_LVM_MAX_THPOOL_CHUNK_SIZE (1 GiB)
2734 #define BD_LVM_DEFAULT_CHUNK_SIZE (64 KiB)
461461 return _lvm_get_thpool_padding(size, pe_size, included)
462462 __all__.append("lvm_get_thpool_padding")
463463
464 _lvm_get_thpool_meta_size = BlockDev.lvm_get_thpool_meta_size
465 @override(BlockDev.lvm_get_thpool_meta_size)
466 def lvm_get_thpool_meta_size(size, chunk_size=0, n_snapshots=0):
467 return _lvm_get_thpool_meta_size(size, chunk_size, n_snapshots)
468 __all__.append("lvm_get_thpool_meta_size")
469
464470 _lvm_pvcreate = BlockDev.lvm_pvcreate
465471 @override(BlockDev.lvm_pvcreate)
466472 def lvm_pvcreate(device, data_alignment=0, metadata_size=0, extra=None, **kwargs):
2121 #include "extra_arg.h"
2222 #include <syslog.h>
2323 #include <stdlib.h>
24 #include <poll.h>
25 #include <fcntl.h>
2426 #include <errno.h>
2527 #include <sys/types.h>
2628 #include <sys/wait.h>
137139 }
138140
139141 return;
140 }
141
142 static void set_c_locale(gpointer user_data __attribute__((unused))) {
143 if (setenv ("LC_ALL", "C", 1) != 0)
144 g_warning ("Failed to set LC_ALL=C for a child process!");
145142 }
146143
147144 /**
193190 const BDExtraArg **extra_p = NULL;
194191 gint exit_status = 0;
195192 guint i = 0;
193 gchar **old_env = NULL;
194 gchar **new_env = NULL;
196195
197196 if (extra) {
198197 args_len = g_strv_length ((gchar **) argv);
218217 args[i] = NULL;
219218 }
220219
220 old_env = g_get_environ ();
221 new_env = g_environ_setenv (old_env, "LC_ALL", "C", TRUE);
222
221223 task_id = log_running (args ? args : argv);
222 success = g_spawn_sync (NULL, args ? (gchar **) args : (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
223 (GSpawnChildSetupFunc) set_c_locale, NULL,
224 &stdout_data, &stderr_data, &exit_status, error);
224 success = g_spawn_sync (NULL, args ? (gchar **) args : (gchar **) argv, new_env, G_SPAWN_SEARCH_PATH,
225 NULL, NULL, &stdout_data, &stderr_data, &exit_status, error);
225226 if (!success) {
226227 /* error is already populated from the call */
228 g_strfreev (new_env);
227229 g_free (stdout_data);
228230 g_free (stderr_data);
229231 return FALSE;
230232 }
233 g_strfreev (new_env);
231234
232235 /* g_spawn_sync set the status in the same way waitpid() does, we need
233236 to get the process exit code manually (this is similar to calling
266269
267270 g_free (stdout_data);
268271 g_free (stderr_data);
272 return TRUE;
273 }
274
275 /* buffer size in bytes used to read from stdout and stderr */
276 #define _EXEC_BUF_SIZE 64*1024
277
278 /* similar to g_strstr_len() yet treats 'null' byte as @needle. */
279 static gchar *bd_strchr_len_null (const gchar *haystack, gssize haystack_len, const gchar needle) {
280 gchar *ret;
281 gchar *ret_null;
282
283 ret = memchr (haystack, needle, haystack_len);
284 ret_null = memchr (haystack, 0, haystack_len);
285 if (ret && ret_null)
286 return MIN (ret, ret_null);
287 else
288 return MAX (ret, ret_null);
289 }
290
291 static gboolean
292 _process_fd_event (gint fd, struct pollfd *poll_fd, GString *read_buffer, GString *filtered_buffer, gsize *read_buffer_pos, gboolean *done,
293 guint64 progress_id, guint8 *progress, BDUtilsProgExtract prog_extract, GError **error) {
294 gchar buf[_EXEC_BUF_SIZE] = { 0 };
295 ssize_t num_read;
296 gchar *line;
297 gchar *newline_pos;
298 int errno_saved;
299 gboolean eof = FALSE;
300
301 if (! *done && (poll_fd->revents & POLLIN)) {
302 /* read until we get EOF (0) or error (-1), expecting EAGAIN */
303 while ((num_read = read (fd, buf, _EXEC_BUF_SIZE)) > 0)
304 g_string_append_len (read_buffer, buf, num_read);
305 errno_saved = errno;
306
307 /* process the fresh data by lines */
308 if (read_buffer->len > *read_buffer_pos) {
309 gchar *buf_ptr;
310 gsize buf_len;
311
312 while ((buf_ptr = read_buffer->str + *read_buffer_pos,
313 buf_len = read_buffer->len - *read_buffer_pos,
314 newline_pos = bd_strchr_len_null (buf_ptr, buf_len, '\n'))) {
315 line = g_strndup (buf_ptr, newline_pos - buf_ptr + 1);
316 if (prog_extract && prog_extract (line, progress))
317 bd_utils_report_progress (progress_id, *progress, NULL);
318 else
319 g_string_append (filtered_buffer, line);
320 g_free (line);
321 *read_buffer_pos = newline_pos - read_buffer->str + 1;
322 }
323 }
324
325 /* read error */
326 if (num_read < 0 && errno_saved != EAGAIN && errno_saved != EINTR) {
327 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
328 "Error reading from pipe: %s", g_strerror (errno_saved));
329 return FALSE;
330 }
331
332 /* EOF */
333 if (num_read == 0)
334 eof = TRUE;
335 }
336
337 if (poll_fd->revents & POLLHUP || poll_fd->revents & POLLERR || poll_fd->revents & POLLNVAL)
338 eof = TRUE;
339
340 if (eof) {
341 *done = TRUE;
342 /* process the remaining buffer */
343 line = read_buffer->str + *read_buffer_pos;
344 /* GString guarantees the buffer is always NULL-terminated. */
345 if (strlen (line) > 0) {
346 if (prog_extract && prog_extract (line, progress))
347 bd_utils_report_progress (progress_id, *progress, NULL);
348 else
349 g_string_append (filtered_buffer, line);
350 }
351 }
352
269353 return TRUE;
270354 }
271355
280364 gchar *msg = NULL;
281365 GPid pid = 0;
282366 gint out_fd = 0;
283 GIOChannel *out_pipe = NULL;
284367 gint err_fd = 0;
285 GIOChannel *err_pipe = NULL;
286 gchar *line = NULL;
287368 gint child_ret = -1;
288369 gint status = 0;
289370 gboolean ret = FALSE;
290 GIOStatus io_status = G_IO_STATUS_NORMAL;
291371 gint poll_status = 0;
292372 guint i = 0;
293373 guint8 completion = 0;
294 GPollFD fds[2] = {ZERO_INIT, ZERO_INIT};
374 struct pollfd fds[2] = { ZERO_INIT, ZERO_INIT };
375 int flags;
295376 gboolean out_done = FALSE;
296377 gboolean err_done = FALSE;
297 GString *stdout_data = g_string_new (NULL);
298 GString *stderr_data = g_string_new (NULL);
378 GString *stdout_data;
379 GString *stdout_buffer;
380 GString *stderr_data;
381 GString *stderr_buffer;
382 gsize stdout_buffer_pos = 0;
383 gsize stderr_buffer_pos = 0;
384 gchar **old_env = NULL;
385 gchar **new_env = NULL;
386 gboolean success = TRUE;
299387
300388 /* TODO: share this code between functions */
301389 if (extra) {
324412
325413 task_id = log_running (args ? args : argv);
326414
327 ret = g_spawn_async_with_pipes (NULL, args ? (gchar**) args : (gchar**) argv, NULL,
415 old_env = g_get_environ ();
416 new_env = g_environ_setenv (old_env, "LC_ALL", "C", TRUE);
417
418 ret = g_spawn_async_with_pipes (NULL, args ? (gchar**) args : (gchar**) argv, new_env,
328419 G_SPAWN_DEFAULT|G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD,
329420 NULL, NULL, &pid, NULL, &out_fd, &err_fd, error);
330421
422 g_strfreev (new_env);
423
331424 if (!ret) {
332425 /* error is already populated */
333 g_string_free (stdout_data, TRUE);
334 g_string_free (stderr_data, TRUE);
335426 g_free (args);
336427 return FALSE;
337428 }
343434 g_free (args);
344435 g_free (msg);
345436
346 out_pipe = g_io_channel_unix_new (out_fd);
347 err_pipe = g_io_channel_unix_new (err_fd);
348
349 g_io_channel_set_encoding (out_pipe, NULL, NULL);
350 g_io_channel_set_encoding (err_pipe, NULL, NULL);
437 /* set both fds for non-blocking read */
438 flags = fcntl (out_fd, F_GETFL, 0);
439 if (fcntl (out_fd, F_SETFL, flags | O_NONBLOCK))
440 g_warning ("_utils_exec_and_report_progress: Failed to set out_fd non-blocking: %m");
441 flags = fcntl (err_fd, F_GETFL, 0);
442 if (fcntl (err_fd, F_SETFL, flags | O_NONBLOCK))
443 g_warning ("_utils_exec_and_report_progress: Failed to set err_fd non-blocking: %m");
444
445 stdout_data = g_string_new (NULL);
446 stdout_buffer = g_string_new (NULL);
447 stderr_data = g_string_new (NULL);
448 stderr_buffer = g_string_new (NULL);
351449
352450 fds[0].fd = out_fd;
353451 fds[1].fd = err_fd;
354 fds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
355 fds[1].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
356 while (!out_done || !err_done) {
357 poll_status = g_poll (fds, 2, -1);
452 fds[0].events = POLLIN | POLLHUP | POLLERR;
453 fds[1].events = POLLIN | POLLHUP | POLLERR;
454 while (! (out_done && err_done)) {
455 poll_status = poll (fds, 2, -1 /* timeout */);
456 g_warn_if_fail (poll_status != 0); /* no timeout specified, zero should never be returned */
358457 if (poll_status < 0) {
458 if (errno == EAGAIN || errno == EINTR)
459 continue;
359460 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
360 "Failed to poll output FDs.");
461 "Failed to poll output FDs: %m");
361462 bd_utils_report_finished (progress_id, (*error)->message);
362 g_io_channel_shutdown (out_pipe, FALSE, NULL);
363 g_io_channel_unref (out_pipe);
364 g_io_channel_shutdown (err_pipe, FALSE, NULL);
365 g_io_channel_unref (err_pipe);
366 g_string_free (stdout_data, TRUE);
367 g_string_free (stderr_data, TRUE);
368 return FALSE;
369 } else if (poll_status != 2)
370 /* both revents fields were not filled yet */
371 continue;
372 if (!(fds[0].revents & G_IO_IN))
373 out_done = TRUE;
374 while (!out_done) {
375 io_status = g_io_channel_read_line (out_pipe, &line, NULL, NULL, error);
376 if (io_status == G_IO_STATUS_NORMAL) {
377 if (prog_extract && prog_extract (line, &completion))
378 bd_utils_report_progress (progress_id, completion, NULL);
379 else
380 g_string_append (stdout_data, line);
381 g_free (line);
382 } else if (io_status == G_IO_STATUS_EOF) {
383 out_done = TRUE;
384 } else if (error && (*error)) {
463 success = FALSE;
464 break;
465 }
466
467 if (!out_done) {
468 if (! _process_fd_event (out_fd, &fds[0], stdout_buffer, stdout_data, &stdout_buffer_pos, &out_done, progress_id, &completion, prog_extract, error)) {
385469 bd_utils_report_finished (progress_id, (*error)->message);
386 g_io_channel_shutdown (out_pipe, FALSE, NULL);
387 g_io_channel_unref (out_pipe);
388 g_io_channel_shutdown (err_pipe, FALSE, NULL);
389 g_io_channel_unref (err_pipe);
390 g_string_free (stdout_data, TRUE);
391 g_string_free (stderr_data, TRUE);
392 return FALSE;
470 success = FALSE;
471 break;
393472 }
394473 }
395 if (!(fds[1].revents & G_IO_IN))
396 err_done = TRUE;
397 while (!err_done) {
398 io_status = g_io_channel_read_line (err_pipe, &line, NULL, NULL, error);
399 if (io_status == G_IO_STATUS_NORMAL) {
400 g_string_append (stderr_data, line);
401 g_free (line);
402 } else if (io_status == G_IO_STATUS_EOF) {
403 err_done = TRUE;
404 } else if (error && (*error)) {
474
475 if (!err_done) {
476 if (! _process_fd_event (err_fd, &fds[1], stderr_buffer, stderr_data, &stderr_buffer_pos, &err_done, progress_id, &completion, prog_extract, error)) {
405477 bd_utils_report_finished (progress_id, (*error)->message);
406 g_io_channel_shutdown (out_pipe, FALSE, NULL);
407 g_io_channel_unref (out_pipe);
408 g_io_channel_shutdown (err_pipe, FALSE, NULL);
409 g_io_channel_unref (err_pipe);
410 g_string_free (stdout_data, TRUE);
411 g_string_free (stderr_data, TRUE);
412 return FALSE;
478 success = FALSE;
479 break;
413480 }
414481 }
415482 }
416483
484 g_string_free (stdout_buffer, TRUE);
485 g_string_free (stderr_buffer, TRUE);
486 close (out_fd);
487 close (err_fd);
488
417489 child_ret = waitpid (pid, &status, 0);
418 *proc_status = WEXITSTATUS(status);
419 if (child_ret > 0) {
420 if (*proc_status != 0) {
421 if (stderr_data->str && (g_strcmp0 ("", stderr_data->str) != 0))
422 msg = stderr_data->str;
423 else
424 msg = stdout_data->str;
425 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
426 "Process reported exit code %d: %s", *proc_status, msg);
427 bd_utils_report_finished (progress_id, (*error)->message);
428 g_io_channel_shutdown (out_pipe, FALSE, NULL);
429 g_io_channel_unref (out_pipe);
430 g_io_channel_shutdown (err_pipe, FALSE, NULL);
431 g_io_channel_unref (err_pipe);
432 g_string_free (stdout_data, TRUE);
433 g_string_free (stderr_data, TRUE);
434 return FALSE;
435 }
436 if (WIFSIGNALED(status)) {
437 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
438 "Process killed with a signal");
439 bd_utils_report_finished (progress_id, (*error)->message);
440 g_io_channel_shutdown (out_pipe, FALSE, NULL);
441 g_io_channel_unref (out_pipe);
442 g_io_channel_shutdown (err_pipe, FALSE, NULL);
443 g_io_channel_unref (err_pipe);
444 g_string_free (stdout_data, TRUE);
445 g_string_free (stderr_data, TRUE);
446 return FALSE;
447 }
448 } else if (child_ret == -1) {
449 if (errno != ECHILD) {
450 errno = 0;
451 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
452 "Failed to wait for the process");
453 bd_utils_report_finished (progress_id, (*error)->message);
454 g_io_channel_shutdown (out_pipe, FALSE, NULL);
455 g_io_channel_unref (out_pipe);
456 g_io_channel_shutdown (err_pipe, FALSE, NULL);
457 g_io_channel_unref (err_pipe);
458 g_string_free (stdout_data, TRUE);
459 g_string_free (stderr_data, TRUE);
460 return FALSE;
461 }
462 /* no such process (the child exited before we tried to wait for it) */
463 errno = 0;
464 }
465
466 bd_utils_report_finished (progress_id, "Completed");
490 *proc_status = WEXITSTATUS (status);
491 if (success) {
492 if (child_ret > 0) {
493 if (*proc_status != 0) {
494 msg = stderr_data->len > 0 ? stderr_data->str : stdout_data->str;
495 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
496 "Process reported exit code %d: %s", *proc_status, msg);
497 bd_utils_report_finished (progress_id, (*error)->message);
498 success = FALSE;
499 } else if (WIFSIGNALED (status)) {
500 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
501 "Process killed with a signal");
502 bd_utils_report_finished (progress_id, (*error)->message);
503 success = FALSE;
504 }
505 } else if (child_ret == -1) {
506 if (errno != ECHILD) {
507 errno = 0;
508 g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
509 "Failed to wait for the process");
510 bd_utils_report_finished (progress_id, (*error)->message);
511 success = FALSE;
512 } else {
513 /* no such process (the child exited before we tried to wait for it) */
514 errno = 0;
515 }
516 }
517 if (success)
518 bd_utils_report_finished (progress_id, "Completed");
519 }
467520 log_out (task_id, stdout_data->str, stderr_data->str);
468521 log_done (task_id, *proc_status);
469522
470 /* we don't care about the status here */
471 g_io_channel_shutdown (out_pipe, FALSE, error);
472 g_io_channel_unref (out_pipe);
473 g_io_channel_shutdown (err_pipe, FALSE, error);
474 g_io_channel_unref (err_pipe);
475
476 if (stdout)
523 if (success && stdout)
477524 *stdout = g_string_free (stdout_data, FALSE);
478525 else
479526 g_string_free (stdout_data, TRUE);
480 if (stderr)
527 if (success && stderr)
481528 *stderr = g_string_free (stderr_data, FALSE);
482529 else
483530 g_string_free (stderr_data, TRUE);
484531
485 return TRUE;
532 return success;
486533 }
487534
488535 /**
489536 * bd_utils_exec_and_report_progress:
490537 * @argv: (array zero-terminated=1): the argv array for the call
491538 * @extra: (allow-none) (array zero-terminated=1): extra arguments
492 * @prog_extract: (scope notified): function for extracting progress information
539 * @prog_extract: (scope notified) (nullable): function for extracting progress information
493540 * @proc_status: (out): place to store the process exit status
494541 * @error: (out): place to store error (if any)
542 *
543 * Note that any NULL bytes read from standard output and standard error
544 * output are treated as separators similar to newlines and @prog_extract
545 * will be called with the respective chunk.
495546 *
496547 * Returns: whether the @argv was successfully executed (no error and exit code 0) or not
497548 */
505556 * @extra: (allow-none) (array zero-terminated=1): extra arguments
506557 * @output: (out): variable to store output to
507558 * @error: (out): place to store error (if any)
559 *
560 * Note that any NULL bytes read from standard output and standard error
561 * output will be discarded.
508562 *
509563 * Returns: whether the @argv was successfully executed capturing the output or not
510564 */
536590 g_free (stderr);
537591 return TRUE;
538592 }
539
540593 }
541594
542595 /**
3030
3131 /**
3232 * BDUtilsProgExtract:
33 * @line: line from extract progress from
33 * @line: line to extract progress from
3434 * @completion: (out): percentage of completion
3535 *
36 * Returns: whether the line was a progress reporting line or not
36 * Callback function used to process a line captured from spawned command's standard
37 * output and standard error output. Typically used to extract completion percentage
38 * of a long-running job.
39 *
40 * Note that both outputs are read simultaneously with no guarantees of message order
41 * this function is called with.
42 *
43 * The value the @completion points to may contain value previously returned from
44 * this callback or zero when called for the first time. This is useful for extractors
45 * where only some kind of a tick mark is printed out as a progress and previous value
46 * is needed to compute an incremented value. It's important to keep in mind that this
47 * function is only called over lines, i.e. progress reporting printing out tick marks
48 * (e.g. dots) without a newline character might not work properly.
49 *
50 * The @line string usually contains trailing newline character, which may be absent
51 * however in case the spawned command exits without printing one. It's guaranteed
52 * this function is called over remaining buffer no matter what the trailing
53 * character is.
54 *
55 * Returns: whether the line was a progress reporting line and should be excluded
56 * from the collected standard output string or not.
3757 */
3858 typedef gboolean (*BDUtilsProgExtract) (const gchar *line, guint8 *completion);
3959
348348
349349 # try reinitializing with only some utilities being available and thus
350350 # only some plugins able to load
351 with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "lvm",
352 "thin_metadata_size", "swaplabel"]):
351 with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "lvm", "swaplabel"]):
353352 succ, loaded = BlockDev.try_reinit(self.requested_plugins, True, None)
354353 self.assertFalse(succ)
355354 for plug_name in ("swap", "lvm", "crypto"):
360359
361360 # now the same with a subset of plugins requested
362361 plugins = BlockDev.plugin_specs_from_names(["lvm", "swap", "crypto"])
363 with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "lvm",
364 "thin_metadata_size", "swaplabel"]):
362 with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "lvm","swaplabel"]):
365363 succ, loaded = BlockDev.try_reinit(plugins, True, None)
366364 self.assertTrue(succ)
367365 self.assertEqual(set(loaded), set(["swap", "lvm", "crypto"]))
140140 def test_get_thpool_meta_size(self):
141141 """Verify that getting recommended thin pool metadata size works as expected"""
142142
143 # no idea how thin_metadata_size works, but let's at least check that
144 # the function works and returns what thin_metadata_size says
145 out1 = subprocess.check_output(["thin_metadata_size", "-ub", "-n", "-b64k", "-s1t", "-m100"])
146 self.assertEqual(int(out1), BlockDev.lvm_get_thpool_meta_size (1 * 1024**4, 64 * 1024, 100))
147
148 out2 = subprocess.check_output(["thin_metadata_size", "-ub", "-n", "-b128k", "-s1t", "-m100"])
149 self.assertEqual(int(out2), BlockDev.lvm_get_thpool_meta_size (1 * 1024**4, 128 * 1024, 100))
150
151 # twice the chunk_size -> roughly half the metadata needed
152 self.assertAlmostEqual(float(out1) / float(out2), 2, places=2)
153
154 # unless thin_metadata_size gives a value that is not valid (too small)
155 self.assertEqual(BlockDev.lvm_get_thpool_meta_size (100 * 1024**2, 128 * 1024, 100),
143 # metadata size is calculated as 64 * pool_size / chunk_size
144 self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 64 * 1024), 1 * 1024**3)
145
146 self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 128 * 1024), 512 * 1024**2)
147
148 # lower limit is 4 MiB
149 self.assertEqual(BlockDev.lvm_get_thpool_meta_size(100 * 1024**2, 128 * 1024),
156150 BlockDev.LVM_MIN_THPOOL_MD_SIZE)
151
152 # upper limit is 31.62 GiB
153 self.assertEqual(BlockDev.lvm_get_thpool_meta_size(100 * 1024**4, 64 * 1024),
154 BlockDev.LVM_MAX_THPOOL_MD_SIZE)
157155
158156 @tag_test(TestTags.NOSTORAGE)
159157 def test_is_valid_thpool_md_size(self):
160158 """Verify that is_valid_thpool_md_size works as expected"""
161159
162 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(2 * 1024**2))
163 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(3 * 1024**2))
164 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(16 * 1024**3))
160 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(4 * 1024**2))
161 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(5 * 1024**2))
162 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(15 * 1024**3))
165163
166164 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(1 * 1024**2))
167 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(17 * 1024**3))
165 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(3 * 1024**2))
166 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(16 * 1024**3))
167 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(32 * 1024**3))
168168
169169 @tag_test(TestTags.NOSTORAGE)
170170 def test_is_valid_thpool_chunk_size(self):
133133 def test_get_thpool_meta_size(self):
134134 """Verify that getting recommended thin pool metadata size works as expected"""
135135
136 # no idea how thin_metadata_size works, but let's at least check that
137 # the function works and returns what thin_metadata_size says
138 out1 = subprocess.check_output(["thin_metadata_size", "-ub", "-n", "-b64k", "-s1t", "-m100"])
139 self.assertEqual(int(out1), BlockDev.lvm_get_thpool_meta_size (1 * 1024**4, 64 * 1024, 100))
140
141 out2 = subprocess.check_output(["thin_metadata_size", "-ub", "-n", "-b128k", "-s1t", "-m100"])
142 self.assertEqual(int(out2), BlockDev.lvm_get_thpool_meta_size (1 * 1024**4, 128 * 1024, 100))
143
144 # twice the chunk_size -> roughly half the metadata needed
145 self.assertAlmostEqual(float(out1) / float(out2), 2, places=2)
146
147 # unless thin_metadata_size gives a value that is not valid (too small)
148 self.assertEqual(BlockDev.lvm_get_thpool_meta_size (100 * 1024**2, 128 * 1024, 100),
136
137 self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 64 * 1024),
138 1 * 1024**3)
139
140 self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 128 * 1024),
141 512 * 1024**2)
142
143 self.assertEqual(BlockDev.lvm_get_thpool_meta_size(100 * 1024**2, 128 * 1024),
149144 BlockDev.LVM_MIN_THPOOL_MD_SIZE)
150145
151146 @tag_test(TestTags.NOSTORAGE)
152147 def test_is_valid_thpool_md_size(self):
153148 """Verify that is_valid_thpool_md_size works as expected"""
154149
155 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(2 * 1024**2))
156 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(3 * 1024**2))
157 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(16 * 1024**3))
150 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(4 * 1024**2))
151 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(5 * 1024**2))
152 self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(15 * 1024**3))
158153
159154 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(1 * 1024**2))
160 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(17 * 1024**3))
155 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(3 * 1024**2))
156 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(16 * 1024**3))
157 self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(32 * 1024**3))
161158
162159 @tag_test(TestTags.NOSTORAGE)
163160 def test_is_valid_thpool_chunk_size(self):
6969 finally:
7070 os.environ["PATH"] = old_path
7171
72 ALL_UTILS = {"lvm", "thin_metadata_size", "btrfs", "mkswap", "swaplabel", "multipath", "mpathconf", "dmsetup", "mdadm", "make-bcache", "sgdisk", "sfdisk"}
72 ALL_UTILS = {"lvm", "btrfs", "mkswap", "swaplabel", "multipath", "mpathconf", "dmsetup", "mdadm", "make-bcache", "sgdisk", "sfdisk"}
7373
7474 @contextmanager
7575 def fake_path(path=None, keep_utils=None, all_but=None):
00 import unittest
11 import re
22 import os
3 import six
34 import overrides_hack
4 from utils import fake_utils, create_sparse_tempfile, create_lio_device, delete_lio_device, run_command, TestTags, tag_test
5 from utils import fake_utils, create_sparse_tempfile, create_lio_device, delete_lio_device, run_command, TestTags, tag_test, read_file
56
67 from gi.repository import BlockDev, GLib
78
6465 succ = BlockDev.utils_exec_and_report_error(["true"])
6566 self.assertTrue(succ)
6667
68 with six.assertRaisesRegex(self, GLib.GError, r"Process reported exit code 1"):
69 succ = BlockDev.utils_exec_and_report_error(["/bin/false"])
70
6771 succ, out = BlockDev.utils_exec_and_capture_output(["echo", "hi"])
6872 self.assertTrue(succ)
6973 self.assertEqual(out, "hi\n")
168172
169173 # exit code != 0
170174 self.assertTrue(BlockDev.utils_check_util_version("libblockdev-fake-util-fail", "1.1", "version", "Version:\\s(.*)"))
175
176 @tag_test(TestTags.NOSTORAGE, TestTags.CORE)
177 def test_exec_locale(self):
178 """Verify that setting locale for exec functions works as expected"""
179
180 succ, out = BlockDev.utils_exec_and_capture_output(["locale"])
181 self.assertTrue(succ)
182 self.assertIn("LC_ALL=C", out)
183
184 @tag_test(TestTags.NOSTORAGE, TestTags.CORE)
185 def test_exec_buffer_bloat(self):
186 """Verify that very large output from a command is handled properly"""
187
188 # easy 64kB of data
189 cnt = 65536
190 succ, out = BlockDev.utils_exec_and_capture_output(["bash", "-c", "for i in {1..%d}; do echo -n .; done" % cnt])
191 self.assertTrue(succ)
192 self.assertEquals(len(out), cnt)
193
194 succ, out = BlockDev.utils_exec_and_capture_output(["bash", "-c", "for i in {1..%d}; do echo -n .; echo -n \# >&2; done" % cnt])
195 self.assertTrue(succ)
196 self.assertEquals(len(out), cnt)
197
198 # now exceed the system pipe buffer size
199 # pipe(7): The maximum size (in bytes) of individual pipes that can be set by users without the CAP_SYS_RESOURCE capability.
200 cnt = int(read_file("/proc/sys/fs/pipe-max-size")) + 100
201 self.assertGreater(cnt, 0)
202
203 succ, out = BlockDev.utils_exec_and_capture_output(["bash", "-c", "for i in {1..%d}; do echo -n .; done" % cnt])
204 self.assertTrue(succ)
205 self.assertEquals(len(out), cnt)
206
207 succ, out = BlockDev.utils_exec_and_capture_output(["bash", "-c", "for i in {1..%d}; do echo -n .; echo -n \# >&2; done" % cnt])
208 self.assertTrue(succ)
209 self.assertEquals(len(out), cnt)
210
211 # make use of some newlines
212 succ, out = BlockDev.utils_exec_and_capture_output(["bash", "-c", "for i in {1..%d}; do echo -n .; if [ $(($i%%500)) -eq 0 ]; then echo ''; fi; done" % cnt])
213 self.assertTrue(succ)
214 self.assertGreater(len(out), cnt)
215
216 succ, out = BlockDev.utils_exec_and_capture_output(["bash", "-c", "for i in {1..%d}; do echo -n .; echo -n \# >&2; if [ $(($i%%500)) -eq 0 ]; then echo ''; echo '' >&2; fi; done" % cnt])
217 self.assertTrue(succ)
218 self.assertGreater(len(out), cnt)
219
220
221 EXEC_PROGRESS_MSG = "Aloha, I'm the progress line you should match."
222
223 def my_exec_progress_func_concat(self, line):
224 """Expect an concatenated string"""
225 s = ""
226 for i in range(10):
227 s += self.EXEC_PROGRESS_MSG
228 self.assertEqual(line, s)
229 self.num_matches += 1
230 return 0
231
232 def my_exec_progress_func(self, line):
233 self.assertTrue(re.match(r".*%s.*" % self.EXEC_PROGRESS_MSG, line))
234 self.num_matches += 1
235 return 0
236
237 def test_exec_buffer_bloat_progress(self):
238 """Verify that progress reporting can handle large output"""
239
240 # no newlines, should match just a single occurrence on EOF
241 cnt = 10
242 self.num_matches = 0
243 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..10}; do echo -n \"%s\"; done" % self.EXEC_PROGRESS_MSG], None, self.my_exec_progress_func_concat)
244 self.assertTrue(status)
245 self.assertEqual(self.num_matches, 1)
246
247 # ten matches
248 self.num_matches = 0
249 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo \"%s\"; done" % (cnt, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
250 self.assertTrue(status)
251 self.assertEqual(self.num_matches, cnt)
252
253 # 100k matches
254 cnt = 100000
255 self.num_matches = 0
256 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo \"%s\"; done" % (cnt, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
257 self.assertTrue(status)
258 self.assertEqual(self.num_matches, cnt)
259
260 # 100k matches on stderr
261 self.num_matches = 0
262 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo \"%s\" >&2; done" % (cnt, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
263 self.assertTrue(status)
264 self.assertEqual(self.num_matches, cnt)
265
266 # 100k matches on stderr and stdout
267 self.num_matches = 0
268 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo \"%s\"; echo \"%s\" >&2; done" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
269 self.assertTrue(status)
270 self.assertEqual(self.num_matches, cnt * 2)
271
272 # stdout and stderr output, non-zero return from the command and very long exception message
273 self.num_matches = 0
274 with six.assertRaisesRegex(self, GLib.GError, r"Process reported exit code 66"):
275 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo \"%s\"; echo \"%s\" >&2; done; exit 66" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
276 self.assertEqual(self.num_matches, cnt * 2)
277
278 # no progress reporting callback given, tests slightly different code path
279 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo \"%s\"; echo \"%s\" >&2; done" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, None)
280 self.assertTrue(status)
281
282 def test_exec_null_bytes(self):
283 """Verify that null bytes in the output are processed properly"""
284
285 TEST_DATA = ["First line", "Second line", "Third line"]
286
287 status, out = BlockDev.utils_exec_and_capture_output(["bash", "-c", "echo -e \"%s\\0%s\\n%s\"" % (TEST_DATA[0], TEST_DATA[1], TEST_DATA[2])])
288 self.assertTrue(status)
289 self.assertTrue(TEST_DATA[0] in out)
290 self.assertTrue(TEST_DATA[1] in out)
291 self.assertTrue(TEST_DATA[2] in out)
292 self.assertFalse("kuku!" in out)
293
294 # ten matches
295 cnt = 10
296 self.num_matches = 0
297 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo -e \"%s\\0%s\"; done" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
298 self.assertTrue(status)
299 self.assertEqual(self.num_matches, cnt * 2)
300
301 # 100k matches
302 cnt = 100000
303 self.num_matches = 0
304 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo -e \"%s\\0%s\"; done" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
305 self.assertTrue(status)
306 self.assertEqual(self.num_matches, cnt * 2)
307
308 # 100k matches on stderr
309 self.num_matches = 0
310 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo -e \"%s\\0%s\" >&2; done" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
311 self.assertTrue(status)
312 self.assertEqual(self.num_matches, cnt * 2)
313
314 # 100k matches on stderr and stdout
315 self.num_matches = 0
316 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo -e \"%s\\0%s\"; echo -e \"%s\\0%s\" >&2; done" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
317 self.assertTrue(status)
318 self.assertEqual(self.num_matches, cnt * 4)
319
320 # stdout and stderr output, non-zero return from the command and very long exception message
321 self.num_matches = 0
322 with six.assertRaisesRegex(self, GLib.GError, r"Process reported exit code 66"):
323 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo -e \"%s\\0%s\"; echo -e \"%s\\0%s\" >&2; done; exit 66" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, self.my_exec_progress_func)
324 self.assertEqual(self.num_matches, cnt * 4)
325
326 # no progress reporting callback given, tests slightly different code path
327 status = BlockDev.utils_exec_and_report_progress(["bash", "-c", "for i in {1..%d}; do echo -e \"%s\\0%s\"; echo -e \"%s\\0%s\" >&2; done" % (cnt, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG, self.EXEC_PROGRESS_MSG)], None, None)
328 self.assertTrue(status)
171329
172330
173331 class UtilsDevUtilsTestCase(UtilsTestCase):