diff --git a/README.md b/README.md
index 0406869..8f5634b 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Blocksync-fast uses the Libgcrypt library and supports many hashing algorithms,
## Installation
```console
- $ ./configure
+ $ CFLAGS="-Wall -Werror -D_FILE_OFFSET_BITS=64 -O3" LIBS="-lm" ./configure
$ make
$ make install
```
@@ -252,6 +252,48 @@ $ blocksync-fast -d vol-2024-05-06-00.img --apply-delta -D vol-2024-05-06-00.del
$ blocksync-fast -d vol-2024-05-06-00.img --apply-delta -D vol-2024-05-07-00.delta
```
+## DM-Era Support
+
+You can optimize image backups with blocksync-fast when using the device manager "era" feature. With this, you add an extra "era" mapping between a file system and the underlying storage. If you access the "era" mapping instead of the original storage, all write operations are recorded in an extra storage named "era metadata". For details, refer to the Linux kernel document on the topic: https://docs.kernel.org/admin-guide/device-mapper/era.html
+
+You can query which blocks of the "era" mapping where written to since a specified era in the past. If you feed the resulting XML file into blocksync-fast, there will be a substancial operation speed up because only parts of the source device needs to be re-read and checksummed. The "--era file.xml" option is active for "--make-digest" and "--make-delta". Here is an commented example (provided that you have an LVM2 volume group "vg0" with free space as well as root access):
+
+ # Create an empty LV with 1 PE (==4M) size. 4M should be enough for era metadata btree.
+ lvcreate --extents 1 --name era vg0
+ blkdiscard /dev/mapper/vg0-era
+ # Create an LV with 32 PE (==128M) size. This is our image to be backed up.
+ lvcreate --extents 32 --name test vg0
+ # Create device manager mappings
+ dmsetup create test-meta --table "0 8192 linear /dev/mapper/vg0-era 0"
+ dmsetup create test-era --table "0 262144 era /dev/mapper/test-meta /dev/mapper/vg0-test 4096"
+ dmsetup create test-access --table "0 8192 linear /dev/mapper/test-meta 0"
+ # Create a file system (Note: calls blkdiscard ioctl)
+ mkfs.ext4 /dev/mapper/test-era
+ # Take a metadata snapshot, advances era 1 to era 2
+ dmsetup message test-era 0 take_metadata_snap
+ # Query changes during "mkfs"
+ era_invalidate --written-since 1 /dev/mapper/test-access
+
+
+
+
+
+
+
+
+
+
+
+The example above uses era blocks with 4096 sectors (2M) to differentiate from PE size which is 8192 sectors (4M). If you feed the resulting XML into blocksync-fast, the first 2M of /dev/mapper/test-era are checksummed, then 4M skipped, then 4M checksummed, then 14M skipped, and so on...
+
+If you followed the above example, you may want to
+
+ dmsetup remove test-access
+ dmsetup remove test-era
+ dmsetup remove test-meta
+ lvremove vg0/test
+ lvremove vg0/era
+
## Limitations and Notes
Blocksync-fast is a tool for fast synchronization of block devices, designed to improve block-based backups. To use it as an automatic backup tool, it is recommended to include it in a BASH script, which will allow you to set up a solution adjusted to your individual needs, using various features and tools available in Linux. It should be taken into account the possibility of synchronization interruption due to network disconnection, device detachment, or other errors that may occur. In case of synchronization failure, the program will return an error code greater than 0, which should be handled in the BASH shell and appropriate actions, such as generating reports or retrying the synchronization, should be taken. It is also important to note that when synchronization with the Digest file is interrupted, there may be an inconsistencies between the state of the Digest file and the target storage device. Therefore, after each such interruption, it is recommended to rebuild the Digest file from the target backup or operate on a copy of the Digest file until full synchronization is achieved.
diff --git a/src/blocksync-fast.c b/src/blocksync-fast.c
index ede10e6..2078325 100644
--- a/src/blocksync-fast.c
+++ b/src/blocksync-fast.c
@@ -32,8 +32,7 @@ void print_help(void)
fprintf(flag.prst, "\n");
- fprintf(flag.prst, "Usage:\n",
- process_name);
+ fprintf(flag.prst, "Usage:\n");
fprintf(flag.prst, " %s [options]\n",
process_name);
@@ -81,6 +80,15 @@ void print_help(void)
" Delta file path. If none, data write to stdout or read from stdin\n"
"\n"
+ "-e, --era=PATH\n"
+ " Era XML file path. If set, will only process denoted blocks\n"
+ " Note: Era XML does not reflect BLKDISCARD changes (trim, mkfs)\n"
+ "\n"
+
+ "-E, --era-sectors=N\n"
+ " Era block size in sectors (default: 4096)\n"
+ "\n"
+
"-b, --block-size=N[KMG]\n"
" Block size in N bytes for writing and checksum calculations\n"
" (default:4K)\n"
@@ -252,9 +260,9 @@ void print_summary(void)
if (flag.progress > 0)
fprintf(flag.prst, "\n");
- fprintf(flag.prst, "%s: %zu/%zu blocks, %zu/%zu bytes.\n",
+ fprintf(flag.prst, "%s: %zu/%zu blocks, %zu/%llu bytes.\n",
flag.oper_mode == MAKEDIGEST ? (IS_MODE(digest.open_mode, READ) ? "Updated" : "Created") : (IS_MODE(dst.open_mode, READ) ? "Updated" : "Copied"),
- prog.wri_blocks, param.num_blocks, prog.wri_bytes, param.data_size);
+ prog.wri_blocks, param.num_blocks, prog.wri_bytes, (unsigned long long)param.data_size);
if ( flag.oper_mode == BLOCKSYNC && IS_MODE(src.open_mode, PIPE_R) ) {
@@ -294,6 +302,8 @@ void parse_options(int argc, char **argv)
{"size", required_argument, 0, 'S'},
{"digest", required_argument, 0, 'f'},
{"delta", required_argument, 0, 'D'},
+ {"era", required_argument, 0, 'e'},
+ {"era-sectors", required_argument, 0, 'E'},
{"buffer-size", required_argument, 0, 1001},
{"block-size", required_argument, 0, 'b'},
{"algo", required_argument, 0, 'a'},
@@ -342,6 +352,12 @@ void parse_options(int argc, char **argv)
case 'a':
param.hash_algo = optarg;
break;
+ case 'e':
+ param.era = optarg;
+ break;
+ case 'E':
+ param.era_sectors = atoi(optarg);
+ break;
case 1001:
param.max_buf_size = parse_units(optarg);
break;
@@ -580,6 +596,184 @@ void make_delta(void)
dev_truncate(&delta);
}
+void make_delta_era(void)
+{
+ size_t digest_flush = 0;
+ bool dev_reload = false;
+ bool digest_reload = false;
+ bool delta_reload = false;
+
+ FILE *era_fp = stdin;
+ char era_xml[128];
+ char c;
+
+ if (0 != strcmp("-", param.era))
+ {
+ era_fp = fopen(param.era, "r");
+ if (NULL == era_fp)
+ {
+ fprintf(stderr, "%s: %s\n", param.era, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (NULL == fgets(era_xml, sizeof(era_xml), era_fp))
+ {
+ fprintf(stderr, "%s is is empty\n", param.era);
+ exit(EXIT_FAILURE);
+ }
+
+ if (0 >= sscanf(era_xml, "%c", &c) || c != '\n')
+ {
+ era_xml[strcspn(era_xml, "\r\n")] = 0;
+ fprintf(stderr, "%s - 1st line: '' expected, have: %s\n", param.era, era_xml);
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(era_xml, sizeof(era_xml), era_fp) != NULL)
+ {
+ unsigned long long begin;
+ unsigned long long end;
+
+ if (2 == sscanf(era_xml, " ", &begin, &end))
+ {
+ if (begin >= end)
+ {
+ era_xml[strcspn(era_xml, "\r\n")] = 0;
+ fprintf(stderr, "%s - wrong line: %s\n", param.era, era_xml);
+ exit(EXIT_FAILURE);
+ }
+ }
+ else if (1 == sscanf(era_xml, " ", &begin))
+ {
+ end = begin + 1;
+ }
+ else if (0 >= sscanf(era_xml, "%c", &c) || c != '\n')
+ {
+ era_xml[strcspn(era_xml, "\r\n")] = 0;
+ fprintf(stderr, "%s - last line: '' expected, have: %s\n", param.era, era_xml);
+ exit(EXIT_FAILURE);
+ }
+ else
+ {
+ break;
+ }
+
+ while (src.abs_off < src.data_size && src.abs_off < (off_t)end * 512 * param.era_sectors)
+ {
+ if ((src.abs_off + src.block_size) > src.data_size)
+ {
+ src.block_size = src.data_size % src.block_size;
+ delta.block_size = sizeof(u_int64_t) + src.block_size;
+ }
+
+ dev_reload = false;
+ if (src.abs_off >= (off_t)begin * 512 * param.era_sectors)
+ {
+ dev_reload = check_buffer_reload(&src);
+ }
+ digest_reload = check_buffer_reload(&digest);
+ delta_reload = false;
+ if (src.abs_off >= (off_t)begin * 512 * param.era_sectors)
+ {
+ delta_reload = check_buffer_reload(&delta);
+ }
+
+ if (digest_flush > 0 || digest_reload)
+ digest_wri_flush(digest_flush);
+
+ if (delta_reload)
+ {
+ delta.data_size = delta.abs_off + delta.max_buf_size;
+ dev_truncate(&delta);
+ makedelta_wri_flush_buf();
+ map_buffer(&delta);
+ }
+
+ if (digest_reload || delta_reload)
+ {
+ sync_data(&digest);
+ sync_data(&delta);
+ }
+
+ if (dev_reload)
+ map_buffer(&src);
+
+ if (IS_MODE(digest.open_mode, WRITE) && digest_reload)
+ map_buffer(&digest);
+
+ get_ptr(&src);
+ get_ptr(&digest);
+
+ digest_flush = digest.block_size;
+ if (src.abs_off >= (off_t)begin * 512 * param.era_sectors)
+ {
+ prog.c_dst_wri = true;
+ prog.c_dst_mat = false;
+ prog.c_dig_mat = false;
+ if (IS_MODE(digest.open_mode, WRITE))
+ prog.c_dig_wri = true;
+ else
+ prog.c_dig_wri = false;
+
+ digest_flush = 0;
+
+ if (param.hash_use)
+ hash_buffer(param.algo.value, param.algo.library, param.algo.size, (void *)(oper.hash_buf + (digest.rel_off - digest.mov_off)), src.ptr_r, src.block_size);
+
+ if (IS_MODE(digest.open_mode, READ))
+ {
+ if (memcmp(digest.ptr_r, (const void *)(oper.hash_buf + (digest.rel_off - digest.mov_off)), param.algo.size) == 0)
+ {
+ prog.c_dst_wri = false;
+ prog.c_dig_wri = false;
+ prog.c_dig_mat = true;
+ }
+ }
+
+ if (prog.c_dst_wri)
+ {
+ prog.wri_blocks++;
+ prog.wri_bytes += src.block_size;
+ oper.dev_wri_buf_size += src.block_size;
+
+ memcpy((void *)(oper.delta_buf + oper.delta_wri_buf_size), (uint64_t *)&src.abs_off, sizeof(uint64_t));
+ memcpy((void *)(oper.delta_buf + oper.delta_wri_buf_size + sizeof(uint64_t)), (const void *)src.ptr_r, src.block_size);
+ oper.delta_wri_buf_size += delta.block_size;
+
+ delta.abs_off += delta.block_size;
+ delta.rel_off += delta.block_size;
+ }
+
+ if (prog.c_dig_wri)
+ oper.digest_wri_buf_size += digest.block_size;
+ else
+ digest_flush = digest.block_size;
+ }
+
+ if (flag.progress > 1)
+ print_detail_progress();
+ else if (flag.progress == 1)
+ print_progress();
+
+ digest.abs_off += digest.block_size;
+ digest.rel_off += digest.block_size;
+
+ src.abs_off += src.block_size;
+ src.rel_off += src.block_size;
+
+ oper.num_block++;
+ }
+ }
+
+ digest_wri_flush(0);
+ makedelta_wri_flush_buf();
+
+ delta.data_size = delta.abs_off;
+ dev_truncate(&delta);
+ fclose(era_fp);
+}
+
void apply_delta(void)
{
off_t cur_block_off = 0;
@@ -745,13 +939,154 @@ void make_digest(void)
digest_wri_flush(0);
}
+void make_digest_era(void)
+{
+ size_t digest_flush = 0;
+ bool dev_reload = false;
+ bool digest_reload = false;
+
+ FILE *era_fp = stdin;
+ char era_xml[128];
+ char c;
+
+ if (0 != strcmp("-", param.era))
+ {
+ era_fp = fopen(param.era, "r");
+ if (NULL == era_fp)
+ {
+ fprintf(stderr, "%s: %s\n", param.era, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (NULL == fgets(era_xml, sizeof(era_xml), era_fp))
+ {
+ fprintf(stderr, "%s is is empty\n", param.era);
+ exit(EXIT_FAILURE);
+ }
+
+ if (0 >= sscanf(era_xml, "%c", &c) || c != '\n')
+ {
+ era_xml[strcspn(era_xml, "\r\n")] = 0;
+ fprintf(stderr, "%s - 1st line: '' expected, have: %s\n", param.era, era_xml);
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(era_xml, sizeof(era_xml), era_fp) != NULL)
+ {
+ unsigned long long begin;
+ unsigned long long end;
+
+ if (2 == sscanf(era_xml, " ", &begin, &end))
+ {
+ if (begin >= end)
+ {
+ era_xml[strcspn(era_xml, "\r\n")] = 0;
+ fprintf(stderr, "%s - wrong line: %s\n", param.era, era_xml);
+ exit(EXIT_FAILURE);
+ }
+ }
+ else if (1 == sscanf(era_xml, " ", &begin))
+ {
+ end = begin + 1;
+ }
+ else if (0 >= sscanf(era_xml, "%c", &c) || c != '\n')
+ {
+ era_xml[strcspn(era_xml, "\r\n")] = 0;
+ fprintf(stderr, "%s - last line: '' expected, have: %s\n", param.era, era_xml);
+ exit(EXIT_FAILURE);
+ }
+ else
+ {
+ break;
+ }
+
+ while (src.abs_off < src.data_size && src.abs_off < (off_t)end * 512 * param.era_sectors)
+ {
+ if ((src.abs_off + src.block_size) > src.data_size)
+ src.block_size = src.data_size % src.block_size;
+
+ dev_reload = false;
+ if (src.abs_off >= (off_t)begin * 512 * param.era_sectors)
+ {
+ dev_reload = check_buffer_reload(&src);
+ }
+ digest_reload = check_buffer_reload(&digest);
+
+ if (digest_flush > 0 || digest_reload)
+ digest_wri_flush(digest_flush);
+
+ if (digest_reload)
+ sync_data(&digest);
+
+ if (dev_reload)
+ map_buffer(&src);
+
+ if (digest_reload)
+ map_buffer(&digest);
+
+ get_ptr(&src);
+ get_ptr(&digest);
+
+ digest_flush = digest.block_size;
+ if (src.abs_off >= (off_t)begin * 512 * param.era_sectors)
+ {
+ prog.c_dig_mat = false;
+ prog.c_dig_wri = true;
+
+ digest_flush = 0;
+
+ if (param.hash_use)
+ hash_buffer(param.algo.value, param.algo.library, param.algo.size, (void *)(oper.hash_buf + (digest.rel_off - digest.mov_off)), src.ptr_r, src.block_size);
+
+ if (IS_MODE(digest.open_mode, READ))
+ {
+ if (memcmp(digest.ptr_r, (const void *)(oper.hash_buf + (digest.rel_off - digest.mov_off)), param.algo.size) == 0)
+ {
+ prog.c_dig_wri = false;
+ prog.c_dig_mat = true;
+ }
+ }
+
+ if (prog.c_dig_wri)
+ {
+ oper.digest_wri_buf_size += digest.block_size;
+ prog.wri_blocks++;
+ prog.wri_bytes += digest.block_size;
+ }
+ else
+ digest_flush = digest.block_size;
+ }
+
+ if (flag.progress > 1)
+ print_detail_progress();
+ else if (flag.progress == 1)
+ print_progress();
+
+ digest.abs_off += digest.block_size;
+ digest.rel_off += digest.block_size;
+
+ src.abs_off += src.block_size;
+ src.rel_off += src.block_size;
+
+ oper.num_block++;
+ }
+ }
+
+ digest_wri_flush(0);
+ fclose(era_fp);
+}
+
void init_params(void)
{
- if (flag.oper_mode == MAKEDELTA && delta.path == NULL || flag.oper_mode == MAKEDIGEST && digest.path == NULL)
+ if ((flag.oper_mode == MAKEDELTA && delta.path == NULL) || (flag.oper_mode == MAKEDIGEST && digest.path == NULL))
flag.prst = stderr;
- if (flag.silent)
- freopen("/dev/null", "w", flag.prst) != NULL;
+ if (flag.silent && (NULL == freopen("/dev/null", "w", flag.prst)))
+ {
+ fprintf(stderr, "No /dev/null!?\n");
+ cleanup(EXIT_FAILURE);
+ }
init_map_methods();
@@ -781,7 +1116,7 @@ void init_params(void)
if (digest.path != NULL)
init_digest_file();
else
- fprintf(flag.prst, "Warning: works without digest file.\n", process_name);
+ fprintf(flag.prst, "Warning: %s works without digest file.\n", process_name);
if (IS_MODE(digest.open_mode, READ))
dst.open_mode ^= READ;
@@ -816,7 +1151,7 @@ void init_params(void)
}
if (digest.path == NULL)
- fprintf(flag.prst, "Warning: works without digest file.\n", process_name);
+ fprintf(flag.prst, "Warning: %s works without digest file.\n", process_name);
if (param.hash_algo != NULL)
check_algo_param();
@@ -887,7 +1222,7 @@ void init_params(void)
dst.buf_size = (dst.data_size < dst.max_buf_size ? dst.data_size : dst.max_buf_size);
}
- bool buf_adj_delta = false;
+ __attribute__((unused))bool buf_adj_delta = false;
if (flag.oper_mode == MAKEDELTA)
{
@@ -924,7 +1259,7 @@ void init_params(void)
{
digest.block_size = param.algo.size;
digest.max_buf_size = (src.max_buf_size / src.block_size) * digest.block_size;
- bool buf_adj_digest = adjust_buffer(&digest.max_buf_size, digest.block_size);
+ __attribute__((unused))bool buf_adj_digest = adjust_buffer(&digest.max_buf_size, digest.block_size);
if (IS_MODE(digest.open_mode, DIRECT) || IS_MODE(digest.open_mode, PIPE))
digest.buf_data = realloc(digest.buf_data, digest.max_buf_size);
@@ -933,8 +1268,8 @@ void init_params(void)
}
if (param.block_size < src.stat.st_blksize)
- fprintf(flag.prst, "Warning: given block size is smaller than the block size of the source device, which is %zu bytes\n",
- src.stat.st_blksize);
+ fprintf(flag.prst, "Warning: given block size is smaller than the block size of the source device, which is %llu bytes\n",
+ (unsigned long long)src.stat.st_blksize);
}
if (flag.oper_mode == BLOCKSYNC || flag.oper_mode == APPLYDELTA)
@@ -954,14 +1289,14 @@ void init_params(void)
param.algo.symbol, param.algo.size);
if (param.algo.size > param.block_size)
- fprintf(flag.prst, "Warning: block size '%ld' is smaller than hash '%s' size\n", param.block_size, param.algo.symbol);
+ fprintf(flag.prst, "Warning: block size '%zu' is smaller than hash '%s' size\n", param.block_size, param.algo.symbol);
}
- if (param.data_size > (size_t)(1UL * 1024 * 1024 * 1024 * 1024)) {
+ if (param.data_size > (off_t)(1ULL * 1024 * 1024 * 1024 * 1024)) {
param.pro_prec = 2;
param.pro_fact = 100;
}
- else if (param.data_size > (size_t)(100UL * 1024 * 1024 * 1024)) {
+ else if (param.data_size > (off_t)(100ULL * 1024 * 1024 * 1024)) {
param.pro_prec = 1;
param.pro_fact = 10;
}
@@ -1002,7 +1337,10 @@ int main(int argc, char **argv)
case MAKEDELTA:
init_params();
- make_delta();
+ if (NULL != param.era)
+ make_delta_era();
+ else
+ make_delta();
print_summary();
break;
@@ -1014,7 +1352,10 @@ int main(int argc, char **argv)
case MAKEDIGEST:
init_params();
- make_digest();
+ if (NULL != param.era)
+ make_digest_era();
+ else
+ make_digest();
print_summary();
break;
}
diff --git a/src/digest_info.c b/src/digest_info.c
index 93ff7a6..a8a19ed 100644
--- a/src/digest_info.c
+++ b/src/digest_info.c
@@ -63,7 +63,7 @@ void digest_info(void)
digest.buf_data = malloc(digest.max_buf_size);
}
- size_t dds = digest.data_size;
+ off_t dds = digest.data_size;
digest.data_size = HEADER_SIZE;
map_buffer(&digest);
@@ -158,7 +158,7 @@ void delta_info(void)
delta.buf_data = malloc(delta.max_buf_size);
}
- size_t dds = delta.data_size;
+ off_t dds = delta.data_size;
delta.data_size = HEADER_SIZE;
map_buffer(&delta);
diff --git a/src/globals.c b/src/globals.c
index f8514d0..35536c2 100644
--- a/src/globals.c
+++ b/src/globals.c
@@ -78,7 +78,7 @@ const struct symbol_value_desc algos[] =
{"", 0, 0, 0, ""}};
-struct param param = {NULL, D_BLOCK_SIZE, D_BUFFER_SIZE, 0, NULL, 0, 0, 1, "", false, NULL, algos[D_ALGO]};
+struct param param = {NULL, D_BLOCK_SIZE, D_BUFFER_SIZE, 0, NULL, 0, 0, 1, "", false, NULL, algos[D_ALGO], NULL, 4096};
void get_ptr(struct dev *dev)
{
@@ -142,7 +142,7 @@ void map_buffer(struct dev *dev)
if (rbytes < 0)
{
- fprintf(stderr, "%s: error while reading from stdin : %s\n", process_name, dev->path, strerror(errno));
+ fprintf(stderr, "%s: error while reading from stdin : %s\n", process_name, strerror(errno));
cleanup(EXIT_FAILURE);
}
diff --git a/src/globals.h b/src/globals.h
index 77f96c4..c5e0f85 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -21,7 +21,8 @@
#define PROGRAM_NAME "blocksync-fast"
#define AUTHORS \
- "Marcin Koczwara "
+ "Marcin Koczwara " \
+ "Sven-Ola Tuecke "
#define BSF_VERSION "1.07"
#define COPYRIGHT "Copyright (C) 2024 " AUTHORS
@@ -104,7 +105,7 @@ extern struct dev
int fd;
struct stat stat;
off_t abs_off, buf_off, rel_off, mov_off;
- size_t data_size;
+ off_t data_size;
size_t block_size;
size_t buf_size;
size_t max_buf_size;
@@ -177,13 +178,15 @@ extern struct param
size_t max_buf_size;
size_t num_blocks;
char *h_data_size;
- size_t data_size;
+ off_t data_size;
int pro_prec;
int pro_fact;
char pro_form[20];
bool hash_use;
const char *hash_algo;
struct symbol_value_desc algo;
+ const char *era;
+ size_t era_sectors;
} param;
enum oper_modes
diff --git a/src/init.c b/src/init.c
index ab649d8..c085b0d 100644
--- a/src/init.c
+++ b/src/init.c
@@ -120,7 +120,7 @@ void init_src_device(void)
src.path, format_units(src.data_size, true));
}
- if (src.data_size < 1)
+ if (src.data_size == (off_t)-1)
{
fprintf(stderr, "%s: source device is empty\n", process_name);
cleanup(EXIT_FAILURE);
@@ -529,7 +529,7 @@ void init_src_delta(void)
fprintf(flag.prst, "Data to apply-delta is read from STDIN\n");
delta.fd = STDIN_FILENO;
delta.open_mode = PIPE;
- delta.data_size = SIZE_MAX;
+ delta.data_size = LLONG_MAX;
}
delta.open_mode |= READ;
diff --git a/src/utils.c b/src/utils.c
index ec2dd80..f03dfdd 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -19,12 +19,12 @@
#include "globals.h"
#include // ceil
-long parse_units(char *size)
+off_t parse_units(char *size)
{
- long number;
+ off_t number;
char *str;
- number = strtoul(size, &str, 10);
+ number = strtoull(size, &str, 10);
if (strcasecmp(str, "k") == 0 || strcasecmp(str, "kib") == 0)
number *= 1024;
@@ -42,7 +42,7 @@ long parse_units(char *size)
return number;
}
-char *format_units(long long int size, bool show_bytes)
+char *format_units(off_t size, bool show_bytes)
{
char *number_str = malloc(80);
@@ -57,7 +57,7 @@ char *format_units(long long int size, bool show_bytes)
else
{
if (show_bytes)
- sprintf(number_str, ("%llu bytes"), size);
+ sprintf(number_str, ("%llu bytes"), (unsigned long long)size);
else
sprintf(number_str, ("%.0f B"), (double)size);
@@ -68,7 +68,7 @@ char *format_units(long long int size, bool show_bytes)
{
char *number_str2 = malloc(80);
memcpy(number_str2, number_str, 80);
- sprintf(number_str, ("%s, %llu bytes"), number_str2, size);
+ sprintf(number_str, ("%s, %llu bytes"), number_str2, (unsigned long long)size);
}
return number_str;
diff --git a/src/utils.h b/src/utils.h
index 076f4d7..980be98 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -19,8 +19,8 @@
#ifndef UTILS_H
#define UTILS_H
-long parse_units(char *size);
-char *format_units(long long int size, bool show_bytes);
+off_t parse_units(char *size);
+char *format_units(off_t size, bool show_bytes);
off_t p2r(off_t x);
#endif