]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
update bcache code, fsck improvements
[bcachefs-tools-debian] / tools-util.c
1 #include <alloca.h>
2 #include <assert.h>
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <limits.h>
7 #include <linux/fs.h>
8 #include <math.h>
9 #include <stdbool.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/ioctl.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16
17 #include <uuid/uuid.h>
18
19 #include "ccan/crc/crc.h"
20
21 #include "linux/bcache-ioctl.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 = openat(dirfd, path, O_RDONLY);
63
64         if (fd < 0)
65                 die("Unable to open %s\n", path);
66
67         struct stat statbuf;
68         if (fstat(fd, &statbuf) < 0)
69                 die("fstat error\n");
70
71         char *buf = malloc(statbuf.st_size + 1);
72
73         int len = read(fd, buf, statbuf.st_size);
74         if (len < 0)
75                 die("read error while reading from file %s\n", path);
76
77         buf[len] = '\0';
78         if (len && buf[len - 1] == '\n')
79                 buf[len - 1] = '\0';
80
81         close(fd);
82
83         return buf;
84 }
85
86 u64 read_file_u64(int dirfd, const char *path)
87 {
88         char *buf = read_file_str(dirfd, path);
89         u64 ret = strtoll(buf, NULL, 10);
90
91         free(buf);
92         return ret;
93 }
94
95 /* String list options: */
96
97 ssize_t read_string_list_or_die(const char *opt, const char * const list[],
98                                 const char *msg)
99 {
100         ssize_t v = bch_read_string_list(opt, list);
101         if (v < 0)
102                 die("Bad %s %s", msg, opt);
103
104         return v;
105 }
106
107 /* Returns size of file or block device, in units of 512 byte sectors: */
108 u64 get_size(const char *path, int fd)
109 {
110         struct stat statbuf;
111         if (fstat(fd, &statbuf))
112                 die("Error statting %s: %s", path, strerror(errno));
113
114         if (!S_ISBLK(statbuf.st_mode))
115                 return statbuf.st_size >> 9;
116
117         u64 ret;
118         if (ioctl(fd, BLKGETSIZE64, &ret))
119                 die("Error getting block device size on %s: %s\n",
120                     path, strerror(errno));
121
122         return ret >> 9;
123 }
124
125 /* Returns blocksize in units of 512 byte sectors: */
126 unsigned get_blocksize(const char *path, int fd)
127 {
128         struct stat statbuf;
129         if (fstat(fd, &statbuf))
130                 die("Error statting %s: %s", path, strerror(errno));
131
132         if (!S_ISBLK(statbuf.st_mode))
133                 return statbuf.st_blksize >> 9;
134
135         unsigned ret;
136         if (ioctl(fd, BLKPBSZGET, &ret))
137                 die("Error getting blocksize on %s: %s\n",
138                     path, strerror(errno));
139
140         return ret >> 9;
141 }
142
143 /* Global control device: */
144 int bcachectl_open(void)
145 {
146         int fd = open("/dev/bcache-ctl", O_RDWR);
147         if (fd < 0)
148                 die("Can't open bcache device: %s", strerror(errno));
149
150         return fd;
151 }
152
153 /* Filesystem handles (ioctl, sysfs dir): */
154
155 #define SYSFS_BASE "/sys/fs/bcache/"
156
157 struct bcache_handle bcache_fs_open(const char *path)
158 {
159         struct bcache_handle ret;
160         uuid_t tmp;
161
162         if (!uuid_parse(path, tmp)) {
163                 /* It's a UUID, look it up in sysfs: */
164
165                 char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(path) + 1);
166                 sprintf(sysfs, "%s%s", SYSFS_BASE, path);
167
168                 ret.sysfs_fd = open(sysfs, O_RDONLY);
169                 if (!ret.sysfs_fd)
170                         die("Unable to open %s\n", path);
171
172                 char *minor = read_file_str(ret.sysfs_fd, "minor");
173                 char *ctl = alloca(20 + strlen(minor));
174
175                 sprintf(ctl, "/dev/bcache%s-ctl", minor);
176                 free(minor);
177
178                 ret.ioctl_fd = open(ctl, O_RDWR);
179                 if (ret.ioctl_fd < 0)
180                         die("Error opening control device: %s\n",
181                             strerror(errno));
182         } else {
183                 /* It's a path: */
184
185                 ret.ioctl_fd = open(path, O_RDONLY);
186                 if (ret.ioctl_fd < 0)
187                         die("Error opening %s: %s\n",
188                             path, strerror(errno));
189
190                 struct bch_ioctl_query_uuid uuid;
191                 if (ioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid))
192                         die("ioctl error (not a bcache fs?): %s\n",
193                             strerror(errno));
194
195                 char uuid_str[40];
196                 uuid_unparse(uuid.uuid.b, uuid_str);
197
198                 char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(uuid_str) + 1);
199                 sprintf(sysfs, "%s%s", SYSFS_BASE, uuid_str);
200
201                 ret.sysfs_fd = open(sysfs, O_RDONLY);
202                 if (ret.sysfs_fd < 0)
203                         die("Unable to open sysfs dir %s: %s\n",
204                             sysfs, strerror(errno));
205         }
206
207         return ret;
208 }
209
210 bool ask_yn(void)
211 {
212         const char *short_yes = "yY";
213         char *buf = NULL;
214         size_t buflen = 0;
215         bool ret;
216
217         fputs(" (y,n) ", stdout);
218         fflush(stdout);
219
220         if (getline(&buf, &buflen, stdin) < 0)
221                 die("error reading from standard input");
222
223         ret = strchr(short_yes, buf[0]);
224         free(buf);
225         return ret;
226 }