summaryrefslogtreecommitdiff
path: root/src/cow-extent-map.c
blob: d636dad3cac6cffa79d23be0c1c5142af7e30435 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#define _GNU_SOURCE   /* for program_invocation_name */
#include <assert.h>   /* for assert(3p) */
#include <errno.h>    /* for errno, program_invocation_name */
#include <error.h>    /* for error(3gnu) */
#include <fcntl.h>    /* for open(2) and O_RDONLY */
#include <getopt.h>   /* for getopt_long(3gnu), struct option, optind, optarg */
#include <inttypes.h> /* for PRI* */
#include <stdbool.h>  /* for bool, true, false */
#include <stdio.h>    /* for printf(3p), fprintf(3p), stderr */
#include <stdlib.h>   /* for free(3p), exit(3p), EXIT_SUCCESS, EXIT_FAILURE */
#include <string.h>   /* for strcmp(3p) */
#include <unistd.h>   /* for close(2) */

#include "extent-map.h" /* for uint32_t, fiemap stuff */ 

#define EXIT_INVALIDARGUMENT 2
#define errusage(format, ...) do { \
	error(0, 0, format, ## __VA_ARGS__); \
	fprintf(stderr, "Try '%s --help' for more information.\n", program_invocation_name); \
	exit(EXIT_INVALIDARGUMENT); \
} while(0)

bool indent;

int print_extent(struct fiemap_extent extent) {
	char *flagstr = strextentflags(extent.fe_flags);
	printf("%s"
	       "logical=%"PRIu64" "
	       "physical=%"PRIu64" "
	       "length=%"PRIu64" "
	       "flags=0x%08"PRIx32" (%s)\n",
	       indent ? "\t" : "",
	       extent.fe_logical,
	       extent.fe_physical,
	       extent.fe_length,
	       extent.fe_flags,
	       flagstr);
	free(flagstr);
	return 0;
}

void usage() {
	printf("Usage: %s [OPTIONS] FILENAME...\n"
	       "Print a file extent map.\n"
	       "\n"
	       "OPTIONS:\n"
	       "  --stream=data   Map extents in the data stream (default).\n"
	       "  --stream=xattr  Map extents in the extended attribute stream.\n"
	       "  --sync          Sync the file before mapping extents (default).\n"
	       "  --no-sync       Don't sync the file before mapping extents.\n"
	       "  -m, --machine   Display the output in a more machine-friendly format.\n"
	       "  -h, --help      Display this help and exit.\n",
	       program_invocation_name);
}

int main(int argc, char *argv[]) {
	char *stream = "data";
	bool machine = false;
	int sync = 1;
	struct option long_options[] = {
		{"stream",  optional_argument, NULL, 1},
		{"sync",    no_argument,       &sync, 1},
		{"no-sync", no_argument,       &sync, 0},
		{"help",    no_argument,       NULL, 'h'},
		{0},
	};
	int flag;
	while ((flag = getopt_long(argc, argv, "mh", long_options, NULL)) >= 0) {
		switch (flag) {
		case 0:
			break;
		case 1:
			stream = optarg;
			break;
		case 'm':
			machine = true;
			break;
		case 'h':
			usage();
			return EXIT_SUCCESS;
		case '?':
			fprintf(stderr, "Try '%s --help' for more information.\n", program_invocation_name);
			return EXIT_INVALIDARGUMENT;
		default:
			assert(0);
		}
	}
	if (argc - optind < 1)
		errusage("wrong number of arguments");

	uint32_t flags = sync ? FIEMAP_FLAG_SYNC : 0;
	if (strcmp(stream, "data") == 0) {
		/* do nothing */
	} else if (strcmp(stream, "xattr") == 0) {
		flags |= FIEMAP_FLAG_XATTR;
	} else {
		errusage("invalid stream name");
	}

	indent = (argc - optind) > 1 && !machine;

	int status = 0;
	for (int i = optind; i < argc; i++) {
		char *filename = argv[i];
		int fd = open(filename, O_RDONLY);
		if (fd < 0) {
			error(0, errno, "%s", filename);
			status |= EXIT_FAILURE;
			continue;
		}

		if (machine) {
			printf("%s", filename);
			putchar('\0');
		} else if (argc - optind > 1)
			printf("%s:\n", filename);

		int r = fiemap(fd, flags, print_extent);
		if (r < 0) {
			error(0, -r, "%s: FS_IOC_FIEMAP", filename);
			status |= EXIT_FAILURE;
		} else {
			status |= r;
		}
		if (machine)
			putchar('\0');
		close(fd);
	}
	return status;
}