]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/blkdev.cc
update sources to v12.1.0
[ceph.git] / ceph / src / common / blkdev.cc
1 /*
2 * Ceph - scalable distributed file system
3 *
4 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
5 *
6 * This is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software
9 * Foundation. See file COPYING.
10 *
11 */
12
13 #include <errno.h>
14 #include <sys/ioctl.h>
15 #include <sys/stat.h>
16 #include <dirent.h>
17 #include "include/uuid.h"
18
19 #ifdef __linux__
20 #include <linux/fs.h>
21 #include <blkid/blkid.h>
22
23 #define UUID_LEN 36
24
25 static const char *sandbox_dir = "";
26
27 void set_block_device_sandbox_dir(const char *dir)
28 {
29 if (dir)
30 sandbox_dir = dir;
31 else
32 sandbox_dir = "";
33 }
34
35 int get_block_device_size(int fd, int64_t *psize)
36 {
37 #ifdef BLKGETSIZE64
38 int ret = ::ioctl(fd, BLKGETSIZE64, psize);
39 #elif defined(BLKGETSIZE)
40 unsigned long sectors = 0;
41 int ret = ::ioctl(fd, BLKGETSIZE, &sectors);
42 *psize = sectors * 512ULL;
43 #else
44 // cppcheck-suppress preprocessorErrorDirective
45 # error "Linux configuration error (get_block_device_size)"
46 #endif
47 if (ret < 0)
48 ret = -errno;
49 return ret;
50 }
51
52 /**
53 * get the base device (strip off partition suffix and /dev/ prefix)
54 * e.g.,
55 * /dev/sda3 -> sda
56 * /dev/cciss/c0d1p2 -> cciss/c0d1
57 * dev can a symbolic link.
58 */
59 int get_block_device_base(const char *dev, char *out, size_t out_len)
60 {
61 struct stat st;
62 int r = 0;
63 DIR *dir;
64 char devname[PATH_MAX] = {0}, fn[PATH_MAX] = {0};
65 char *p;
66 char realname[PATH_MAX] = {0};
67
68 if (strncmp(dev, "/dev/", 5) != 0) {
69 if (realpath(dev, realname) == NULL || (strncmp(realname, "/dev/", 5) != 0)) {
70 return -EINVAL;
71 }
72 }
73
74 if (strlen(realname))
75 strncpy(devname, realname + 5, PATH_MAX - 5);
76 else
77 strncpy(devname, dev + 5, strlen(dev) - 5);
78
79 devname[PATH_MAX - 1] = '\0';
80
81 for (p = devname; *p; ++p)
82 if (*p == '/')
83 *p = '!';
84
85 snprintf(fn, sizeof(fn), "%s/sys/block/%s", sandbox_dir, devname);
86 if (stat(fn, &st) == 0) {
87 if (strlen(devname) + 1 > out_len) {
88 return -ERANGE;
89 }
90 strncpy(out, devname, out_len);
91 return 0;
92 }
93
94 snprintf(fn, sizeof(fn), "%s/sys/block", sandbox_dir);
95 dir = opendir(fn);
96 if (!dir)
97 return -errno;
98
99 struct dirent *de = nullptr;
100 while ((de = ::readdir(dir))) {
101 if (de->d_name[0] == '.')
102 continue;
103 snprintf(fn, sizeof(fn), "%s/sys/block/%s/%s", sandbox_dir, de->d_name,
104 devname);
105
106 if (stat(fn, &st) == 0) {
107 // match!
108 if (strlen(de->d_name) + 1 > out_len) {
109 r = -ERANGE;
110 goto out;
111 }
112 strncpy(out, de->d_name, out_len);
113 r = 0;
114 goto out;
115 }
116 }
117 r = -ENOENT;
118
119 out:
120 closedir(dir);
121 return r;
122 }
123
124 /**
125 * get a block device property as a string
126 *
127 * store property in *val, up to maxlen chars
128 * return 0 on success
129 * return negative error on error
130 */
131 int64_t get_block_device_string_property(const char *devname,
132 const char *property,
133 char *val, size_t maxlen)
134 {
135 char filename[PATH_MAX];
136 snprintf(filename, sizeof(filename),
137 "%s/sys/block/%s/%s", sandbox_dir, devname, property);
138
139 FILE *fp = fopen(filename, "r");
140 if (fp == NULL) {
141 return -errno;
142 }
143
144 int r = 0;
145 if (fgets(val, maxlen - 1, fp)) {
146 // truncate at newline
147 char *p = val;
148 while (*p && *p != '\n')
149 ++p;
150 *p = 0;
151 } else {
152 r = -EINVAL;
153 }
154 fclose(fp);
155 return r;
156 }
157
158 /**
159 * get a block device property
160 *
161 * return the value (we assume it is positive)
162 * return negative error on error
163 */
164 int64_t get_block_device_int_property(const char *devname, const char *property)
165 {
166 char buff[256] = {0};
167 int r = get_block_device_string_property(devname, property, buff, sizeof(buff));
168 if (r < 0)
169 return r;
170 // take only digits
171 for (char *p = buff; *p; ++p) {
172 if (!isdigit(*p)) {
173 *p = 0;
174 break;
175 }
176 }
177 char *endptr = 0;
178 r = strtoll(buff, &endptr, 10);
179 if (endptr != buff + strlen(buff))
180 r = -EINVAL;
181 return r;
182 }
183
184 bool block_device_support_discard(const char *devname)
185 {
186 return get_block_device_int_property(devname, "queue/discard_granularity") > 0;
187 }
188
189 int block_device_discard(int fd, int64_t offset, int64_t len)
190 {
191 uint64_t range[2] = {(uint64_t)offset, (uint64_t)len};
192 return ioctl(fd, BLKDISCARD, range);
193 }
194
195 bool block_device_is_rotational(const char *devname)
196 {
197 return get_block_device_int_property(devname, "queue/rotational") > 0;
198 }
199
200 int block_device_model(const char *devname, char *model, size_t max)
201 {
202 return get_block_device_string_property(devname, "device/model", model, max);
203 }
204
205 int get_device_by_uuid(uuid_d dev_uuid, const char* label, char* partition,
206 char* device)
207 {
208 char uuid_str[UUID_LEN+1];
209 char basename[PATH_MAX];
210 const char* temp_partition_ptr = NULL;
211 blkid_cache cache = NULL;
212 blkid_dev dev = NULL;
213 int rc = 0;
214
215 dev_uuid.print(uuid_str);
216
217 if (blkid_get_cache(&cache, NULL) >= 0)
218 dev = blkid_find_dev_with_tag(cache, label, (const char*)uuid_str);
219 else
220 return -EINVAL;
221
222 if (dev) {
223 temp_partition_ptr = blkid_dev_devname(dev);
224 strncpy(partition, temp_partition_ptr, PATH_MAX);
225 rc = get_block_device_base(partition, basename,
226 sizeof(basename));
227 if (rc >= 0) {
228 strncpy(device, basename, sizeof(basename));
229 rc = 0;
230 } else {
231 rc = -ENODEV;
232 }
233 } else {
234 rc = -EINVAL;
235 }
236
237 /* From what I can tell, blkid_put_cache cleans up dev, which
238 * appears to be a pointer into cache, as well */
239 if (cache)
240 blkid_put_cache(cache);
241 return rc;
242 }
243
244 int get_device_by_fd(int fd, char *partition, char *device, size_t max)
245 {
246 struct stat st;
247 int r = fstat(fd, &st);
248 if (r < 0) {
249 return -EINVAL; // hrm.
250 }
251 dev_t devid = S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev;
252 char *t = blkid_devno_to_devname(devid);
253 if (!t) {
254 return -EINVAL;
255 }
256 strncpy(partition, t, max);
257 free(t);
258 dev_t diskdev;
259 r = blkid_devno_to_wholedisk(devid, device, max, &diskdev);
260 if (r < 0) {
261 return -EINVAL;
262 }
263 return 0;
264 }
265
266 #elif defined(__APPLE__)
267 #include <sys/disk.h>
268
269 int get_block_device_size(int fd, int64_t *psize)
270 {
271 unsigned long blocksize = 0;
272 int ret = ::ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize);
273 if (!ret) {
274 unsigned long nblocks;
275 ret = ::ioctl(fd, DKIOCGETBLOCKCOUNT, &nblocks);
276 if (!ret)
277 *psize = (int64_t)nblocks * blocksize;
278 }
279 if (ret < 0)
280 ret = -errno;
281 return ret;
282 }
283
284 bool block_device_support_discard(const char *devname)
285 {
286 return false;
287 }
288
289 int block_device_discard(int fd, int64_t offset, int64_t len)
290 {
291 return -EOPNOTSUPP;
292 }
293
294 bool block_device_is_rotational(const char *devname)
295 {
296 return false;
297 }
298
299 int get_device_by_uuid(uuid_d dev_uuid, const char* label, char* partition,
300 char* device)
301 {
302 return -EOPNOTSUPP;
303 }
304 #elif defined(__FreeBSD__)
305 #include <sys/disk.h>
306
307 int get_block_device_size(int fd, int64_t *psize)
308 {
309 int ret = ::ioctl(fd, DIOCGMEDIASIZE, psize);
310 if (ret < 0)
311 ret = -errno;
312 return ret;
313 }
314
315 bool block_device_support_discard(const char *devname)
316 {
317 return false;
318 }
319
320 int block_device_discard(int fd, int64_t offset, int64_t len)
321 {
322 return -EOPNOTSUPP;
323 }
324
325 bool block_device_is_rotational(const char *devname)
326 {
327 return false;
328 }
329
330 int get_device_by_uuid(uuid_d dev_uuid, const char* label, char* partition,
331 char* device)
332 {
333 return -EOPNOTSUPP;
334 }
335 int get_device_by_fd(int fd, char *partition, char *device, size_t max)
336 {
337 return -EOPNOTSUPP;
338 }
339 #else
340 int get_block_device_size(int fd, int64_t *psize)
341 {
342 return -EOPNOTSUPP;
343 }
344
345 bool block_device_support_discard(const char *devname)
346 {
347 return false;
348 }
349
350 int block_device_discard(int fd, int64_t offset, int64_t len)
351 {
352 return -EOPNOTSUPP;
353 }
354
355 bool block_device_is_rotational(const char *devname)
356 {
357 return false;
358 }
359
360 int get_device_by_uuid(uuid_d dev_uuid, const char* label, char* partition,
361 char* device)
362 {
363 return -EOPNOTSUPP;
364 }
365
366 int get_device_by_fd(int fd, char *partition, char *device, size_t max)
367 {
368 return -EOPNOTSUPP;
369 }
370 #endif