]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
Add a command to dump filesystem metadata
[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: */
108 u64 get_size(const char *path, int fd)
109 {
110         struct stat statbuf;
111         u64 ret;
112
113         if (fstat(fd, &statbuf))
114                 die("Error statting %s: %s", path, strerror(errno));
115
116         if (!S_ISBLK(statbuf.st_mode))
117                 return statbuf.st_size;
118
119         if (ioctl(fd, BLKGETSIZE64, &ret))
120                 die("Error getting block device size on %s: %s\n",
121                     path, strerror(errno));
122
123         return ret;
124 }
125
126 /* Returns blocksize in units of 512 byte sectors: */
127 unsigned get_blocksize(const char *path, int fd)
128 {
129         struct stat statbuf;
130         if (fstat(fd, &statbuf))
131                 die("Error statting %s: %s", path, strerror(errno));
132
133         if (!S_ISBLK(statbuf.st_mode))
134                 return statbuf.st_blksize >> 9;
135
136         unsigned ret;
137         if (ioctl(fd, BLKPBSZGET, &ret))
138                 die("Error getting blocksize on %s: %s\n",
139                     path, strerror(errno));
140
141         return ret >> 9;
142 }
143
144 /* Global control device: */
145 int bcachectl_open(void)
146 {
147         int fd = open("/dev/bcache-ctl", O_RDWR);
148         if (fd < 0)
149                 die("Can't open bcache device: %s", strerror(errno));
150
151         return fd;
152 }
153
154 /* Filesystem handles (ioctl, sysfs dir): */
155
156 #define SYSFS_BASE "/sys/fs/bcache/"
157
158 struct bcache_handle bcache_fs_open(const char *path)
159 {
160         struct bcache_handle ret;
161         uuid_t tmp;
162
163         if (!uuid_parse(path, tmp)) {
164                 /* It's a UUID, look it up in sysfs: */
165
166                 char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(path) + 1);
167                 sprintf(sysfs, "%s%s", SYSFS_BASE, path);
168
169                 ret.sysfs_fd = open(sysfs, O_RDONLY);
170                 if (!ret.sysfs_fd)
171                         die("Unable to open %s\n", path);
172
173                 char *minor = read_file_str(ret.sysfs_fd, "minor");
174                 char *ctl = alloca(20 + strlen(minor));
175
176                 sprintf(ctl, "/dev/bcache%s-ctl", minor);
177                 free(minor);
178
179                 ret.ioctl_fd = open(ctl, O_RDWR);
180                 if (ret.ioctl_fd < 0)
181                         die("Error opening control device: %s\n",
182                             strerror(errno));
183         } else {
184                 /* It's a path: */
185
186                 ret.ioctl_fd = open(path, O_RDONLY);
187                 if (ret.ioctl_fd < 0)
188                         die("Error opening %s: %s\n",
189                             path, strerror(errno));
190
191                 struct bch_ioctl_query_uuid uuid;
192                 if (ioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid))
193                         die("ioctl error (not a bcache fs?): %s\n",
194                             strerror(errno));
195
196                 char uuid_str[40];
197                 uuid_unparse(uuid.uuid.b, uuid_str);
198
199                 char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(uuid_str) + 1);
200                 sprintf(sysfs, "%s%s", SYSFS_BASE, uuid_str);
201
202                 ret.sysfs_fd = open(sysfs, O_RDONLY);
203                 if (ret.sysfs_fd < 0)
204                         die("Unable to open sysfs dir %s: %s\n",
205                             sysfs, strerror(errno));
206         }
207
208         return ret;
209 }
210
211 bool ask_yn(void)
212 {
213         const char *short_yes = "yY";
214         char *buf = NULL;
215         size_t buflen = 0;
216         bool ret;
217
218         fputs(" (y,n) ", stdout);
219         fflush(stdout);
220
221         if (getline(&buf, &buflen, stdin) < 0)
222                 die("error reading from standard input");
223
224         ret = strchr(short_yes, buf[0]);
225         free(buf);
226         return ret;
227 }