]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
cmd_migrate
[bcachefs-tools-debian] / tools-util.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <linux/fs.h>
7 #include <math.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/ioctl.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include <uuid/uuid.h>
17
18 #include "ccan/crc/crc.h"
19
20 #include "linux/bcache-ioctl.h"
21 #include "linux/sort.h"
22 #include "tools-util.h"
23 #include "util.h"
24
25 /* Integer stuff: */
26
27 struct units_buf __pr_units(u64 v, enum units units)
28 {
29         struct units_buf ret;
30
31         switch (units) {
32         case BYTES:
33                 snprintf(ret.b, sizeof(ret.b), "%llu", v << 9);
34                 break;
35         case SECTORS:
36                 snprintf(ret.b, sizeof(ret.b), "%llu", v);
37                 break;
38         case HUMAN_READABLE:
39                 v <<= 9;
40
41                 if (v >= 1024) {
42                         int exp = log(v) / log(1024);
43                         snprintf(ret.b, sizeof(ret.b), "%.1f%c",
44                                  v / pow(1024, exp),
45                                  "KMGTPE"[exp-1]);
46                 } else {
47                         snprintf(ret.b, sizeof(ret.b), "%llu", v);
48                 }
49
50                 break;
51         }
52
53         return ret;
54 }
55
56 /* Argument parsing stuff: */
57
58 /* File parsing (i.e. sysfs) */
59
60 char *read_file_str(int dirfd, const char *path)
61 {
62         int fd = xopenat(dirfd, path, O_RDONLY);
63         size_t len = xfstat(fd).st_size;
64
65         char *buf = malloc(len + 1);
66
67         xpread(fd, buf, len, 0);
68
69         buf[len] = '\0';
70         if (len && buf[len - 1] == '\n')
71                 buf[len - 1] = '\0';
72
73         close(fd);
74
75         return buf;
76 }
77
78 u64 read_file_u64(int dirfd, const char *path)
79 {
80         char *buf = read_file_str(dirfd, path);
81         u64 ret = strtoll(buf, NULL, 10);
82
83         free(buf);
84         return ret;
85 }
86
87 /* String list options: */
88
89 ssize_t read_string_list_or_die(const char *opt, const char * const list[],
90                                 const char *msg)
91 {
92         ssize_t v = bch_read_string_list(opt, list);
93         if (v < 0)
94                 die("Bad %s %s", msg, opt);
95
96         return v;
97 }
98
99 /* Returns size of file or block device: */
100 u64 get_size(const char *path, int fd)
101 {
102         struct stat statbuf = xfstat(fd);
103
104         if (!S_ISBLK(statbuf.st_mode))
105                 return statbuf.st_size;
106
107         u64 ret;
108         xioctl(fd, BLKGETSIZE64, &ret);
109         return ret;
110 }
111
112 /* Returns blocksize in units of 512 byte sectors: */
113 unsigned get_blocksize(const char *path, int fd)
114 {
115         struct stat statbuf = xfstat(fd);
116
117         if (!S_ISBLK(statbuf.st_mode))
118                 return statbuf.st_blksize >> 9;
119
120         unsigned ret;
121         xioctl(fd, BLKPBSZGET, &ret);
122         return ret >> 9;
123 }
124
125 /* Global control device: */
126 int bcachectl_open(void)
127 {
128         return xopen("/dev/bcache-ctl", O_RDWR);
129 }
130
131 /* Filesystem handles (ioctl, sysfs dir): */
132
133 #define SYSFS_BASE "/sys/fs/bcache/"
134
135 struct bcache_handle bcache_fs_open(const char *path)
136 {
137         struct bcache_handle ret;
138         uuid_t tmp;
139
140         if (!uuid_parse(path, tmp)) {
141                 /* It's a UUID, look it up in sysfs: */
142                 char *sysfs = mprintf("%s%s", SYSFS_BASE, path);
143                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
144
145                 char *minor = read_file_str(ret.sysfs_fd, "minor");
146                 char *ctl = mprintf("/dev/bcache%s-ctl", minor);
147                 ret.ioctl_fd = xopen(ctl, O_RDWR);
148
149                 free(sysfs);
150                 free(minor);
151                 free(ctl);
152         } else {
153                 /* It's a path: */
154                 ret.ioctl_fd = xopen(path, O_RDONLY);
155
156                 struct bch_ioctl_query_uuid uuid;
157                 xioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid);
158
159                 char uuid_str[40];
160                 uuid_unparse(uuid.uuid.b, uuid_str);
161
162                 char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str);
163                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
164                 free(sysfs);
165         }
166
167         return ret;
168 }
169
170 bool ask_yn(void)
171 {
172         const char *short_yes = "yY";
173         char *buf = NULL;
174         size_t buflen = 0;
175         bool ret;
176
177         fputs(" (y,n) ", stdout);
178         fflush(stdout);
179
180         if (getline(&buf, &buflen, stdin) < 0)
181                 die("error reading from standard input");
182
183         ret = strchr(short_yes, buf[0]);
184         free(buf);
185         return ret;
186 }
187
188 static int range_cmp(const void *_l, const void *_r)
189 {
190         const struct range *l = _l, *r = _r;
191
192         if (l->start < r->start)
193                 return -1;
194         if (l->start > r->start)
195                 return  1;
196         return 0;
197 }
198
199 void ranges_sort_merge(ranges *r)
200 {
201         struct range *t, *i;
202         ranges tmp = { NULL };
203
204         sort(&darray_item(*r, 0), darray_size(*r),
205              sizeof(darray_item(*r, 0)), range_cmp, NULL);
206
207         /* Merge contiguous ranges: */
208         darray_foreach(i, *r) {
209                 t = tmp.size ?  &tmp.item[tmp.size - 1] : NULL;
210
211                 if (t && t->end >= i->start)
212                         t->end = max(t->end, i->end);
213                 else
214                         darray_append(tmp, *i);
215         }
216
217         darray_free(*r);
218         *r = tmp;
219 }
220
221 void ranges_roundup(ranges *r, unsigned block_size)
222 {
223         struct range *i;
224
225         darray_foreach(i, *r) {
226                 i->start = round_down(i->start, block_size);
227                 i->end  = round_up(i->end, block_size);
228         }
229 }
230
231 void ranges_rounddown(ranges *r, unsigned block_size)
232 {
233         struct range *i;
234
235         darray_foreach(i, *r) {
236                 i->start = round_up(i->start, block_size);
237                 i->end  = round_down(i->end, block_size);
238                 i->end  = max(i->end, i->start);
239         }
240 }
241
242 struct fiemap_extent fiemap_iter_next(struct fiemap_iter *iter)
243 {
244         struct fiemap_extent e;
245
246         BUG_ON(iter->idx > iter->f.fm_mapped_extents);
247
248         if (iter->idx == iter->f.fm_mapped_extents) {
249                 xioctl(iter->fd, FS_IOC_FIEMAP, &iter->f);
250
251                 if (!iter->f.fm_mapped_extents)
252                         return (struct fiemap_extent) { .fe_length = 0 };
253
254                 iter->idx = 0;
255         }
256
257         e = iter->f.fm_extents[iter->idx++];
258         BUG_ON(!e.fe_length);
259
260         iter->f.fm_start = e.fe_logical + e.fe_length;
261
262         return e;
263 }
264
265 const char *strcmp_prefix(const char *a, const char *a_prefix)
266 {
267         while (*a_prefix && *a == *a_prefix) {
268                 a++;
269                 a_prefix++;
270         }
271         return *a_prefix ? NULL : a;
272 }