Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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