]> git.proxmox.com Git - ceph.git/blob - ceph/src/pmdk/src/tools/pmempool/create.c
import ceph 16.2.7
[ceph.git] / ceph / src / pmdk / src / tools / pmempool / create.c
1 // SPDX-License-Identifier: BSD-3-Clause
2 /* Copyright 2014-2019, Intel Corporation */
3
4 /*
5 * create.c -- pmempool create command source file
6 */
7 #include <stdio.h>
8 #include <getopt.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/statvfs.h>
16 #include <errno.h>
17 #include <libgen.h>
18 #include <err.h>
19 #include "common.h"
20 #include "file.h"
21 #include "create.h"
22 #include "os.h"
23
24 #include "set.h"
25 #include "output.h"
26 #include "libpmemblk.h"
27 #include "libpmemlog.h"
28 #include "libpmempool.h"
29
30 #define DEFAULT_MODE 0664
31 /*
32 * pmempool_create -- context and args for create command
33 */
34 struct pmempool_create {
35 int verbose;
36 char *fname;
37 int fexists;
38 char *inherit_fname;
39 int max_size;
40 char *str_type;
41 struct pmem_pool_params params;
42 struct pmem_pool_params inherit_params;
43 char *str_size;
44 char *str_mode;
45 char *str_bsize;
46 uint64_t csize;
47 int write_btt_layout;
48 int force;
49 char *layout;
50 struct options *opts;
51 int clearbadblocks;
52 };
53
54 /*
55 * pmempool_create_default -- default args for create command
56 */
57 static const struct pmempool_create pmempool_create_default = {
58 .verbose = 0,
59 .fname = NULL,
60 .fexists = 0,
61 .inherit_fname = NULL,
62 .max_size = 0,
63 .str_type = NULL,
64 .str_bsize = NULL,
65 .csize = 0,
66 .write_btt_layout = 0,
67 .force = 0,
68 .layout = NULL,
69 .clearbadblocks = 0,
70 .params = {
71 .type = PMEM_POOL_TYPE_UNKNOWN,
72 .size = 0,
73 .mode = DEFAULT_MODE,
74 }
75 };
76
77 /*
78 * help_str -- string for help message
79 */
80 static const char * const help_str =
81 "Create pmem pool of specified size, type and name\n"
82 "\n"
83 "Common options:\n"
84 " -s, --size <size> size of pool\n"
85 " -M, --max-size use maximum available space on file system\n"
86 " -m, --mode <octal> set permissions to <octal> (the default is 0664)\n"
87 " -i, --inherit <file> take required parameters from specified pool file\n"
88 " -b, --clear-bad-blocks clear bad blocks in existing files\n"
89 " -f, --force remove the pool first\n"
90 " -v, --verbose increase verbosity level\n"
91 " -h, --help display this help and exit\n"
92 "\n"
93 "Options for PMEMBLK:\n"
94 " -w, --write-layout force writing the BTT layout\n"
95 "\n"
96 "Options for PMEMOBJ:\n"
97 " -l, --layout <name> layout name stored in pool's header\n"
98 "\n"
99 "For complete documentation see %s-create(1) manual page.\n"
100 ;
101
102 /*
103 * long_options -- command line options
104 */
105 static const struct option long_options[] = {
106 {"size", required_argument, NULL, 's' | OPT_ALL},
107 {"verbose", no_argument, NULL, 'v' | OPT_ALL},
108 {"help", no_argument, NULL, 'h' | OPT_ALL},
109 {"max-size", no_argument, NULL, 'M' | OPT_ALL},
110 {"inherit", required_argument, NULL, 'i' | OPT_ALL},
111 {"mode", required_argument, NULL, 'm' | OPT_ALL},
112 {"write-layout", no_argument, NULL, 'w' | OPT_BLK},
113 {"layout", required_argument, NULL, 'l' | OPT_OBJ},
114 {"force", no_argument, NULL, 'f' | OPT_ALL},
115 {"clear-bad-blocks", no_argument, NULL, 'b' | OPT_ALL},
116 {NULL, 0, NULL, 0 },
117 };
118
119 /*
120 * print_usage -- print application usage short description
121 */
122 static void
123 print_usage(const char *appname)
124 {
125 printf("Usage: %s create [<args>] <blk|log|obj> [<bsize>] <file>\n",
126 appname);
127 }
128
129 /*
130 * print_version -- print version string
131 */
132 static void
133 print_version(const char *appname)
134 {
135 printf("%s %s\n", appname, SRCVERSION);
136 }
137
138 /*
139 * pmempool_create_help -- print help message for create command
140 */
141 void
142 pmempool_create_help(const char *appname)
143 {
144 print_usage(appname);
145 print_version(appname);
146 printf(help_str, appname);
147 }
148
149 /*
150 * pmempool_create_obj -- create pmem obj pool
151 */
152 static int
153 pmempool_create_obj(struct pmempool_create *pcp)
154 {
155 PMEMobjpool *pop = pmemobj_create(pcp->fname, pcp->layout,
156 pcp->params.size, pcp->params.mode);
157 if (!pop) {
158 outv_err("'%s' -- %s\n", pcp->fname, pmemobj_errormsg());
159 return -1;
160 }
161
162 pmemobj_close(pop);
163
164 return 0;
165 }
166
167 /*
168 * pmempool_create_blk -- create pmem blk pool
169 */
170 static int
171 pmempool_create_blk(struct pmempool_create *pcp)
172 {
173 ASSERTne(pcp->params.blk.bsize, 0);
174
175 int ret = 0;
176
177 PMEMblkpool *pbp = pmemblk_create(pcp->fname, pcp->params.blk.bsize,
178 pcp->params.size, pcp->params.mode);
179 if (!pbp) {
180 outv_err("'%s' -- %s\n", pcp->fname, pmemblk_errormsg());
181 return -1;
182 }
183
184 if (pcp->write_btt_layout) {
185 outv(1, "Writing BTT layout using block %d.\n",
186 pcp->write_btt_layout);
187
188 if (pmemblk_set_error(pbp, 0) || pmemblk_set_zero(pbp, 0)) {
189 outv_err("writing BTT layout to block 0 failed\n");
190 ret = -1;
191 }
192 }
193
194 pmemblk_close(pbp);
195
196 return ret;
197 }
198
199 /*
200 * pmempool_create_log -- create pmem log pool
201 */
202 static int
203 pmempool_create_log(struct pmempool_create *pcp)
204 {
205 PMEMlogpool *plp = pmemlog_create(pcp->fname,
206 pcp->params.size, pcp->params.mode);
207
208 if (!plp) {
209 outv_err("'%s' -- %s\n", pcp->fname, pmemlog_errormsg());
210 return -1;
211 }
212
213 pmemlog_close(plp);
214
215 return 0;
216 }
217
218 /*
219 * pmempool_get_max_size -- return maximum allowed size of file
220 */
221 #ifndef _WIN32
222 static int
223 pmempool_get_max_size(const char *fname, uint64_t *sizep)
224 {
225 struct statvfs buf;
226 int ret = 0;
227 char *name = strdup(fname);
228 if (name == NULL) {
229 return -1;
230 }
231
232 char *dir = dirname(name);
233
234 if (statvfs(dir, &buf))
235 ret = -1;
236 else
237 *sizep = buf.f_bsize * buf.f_bavail;
238
239 free(name);
240
241 return ret;
242 }
243 #else
244 static int
245 pmempool_get_max_size(const char *fname, uint64_t *sizep)
246 {
247 int ret = 0;
248 ULARGE_INTEGER freespace;
249 char *name = strdup(fname);
250 if (name == NULL) {
251 return -1;
252 }
253
254 char *dir = dirname(name);
255 wchar_t *str = util_toUTF16(dir);
256 if (str == NULL) {
257 free(name);
258 return -1;
259 }
260 if (GetDiskFreeSpaceExW(str, &freespace, NULL, NULL) == 0)
261 ret = -1;
262 else
263 *sizep = freespace.QuadPart;
264
265 free(str);
266 free(name);
267
268 return ret;
269 }
270 #endif
271
272 /*
273 * print_pool_params -- print some parameters of a pool
274 */
275 static void
276 print_pool_params(struct pmem_pool_params *params)
277 {
278 outv(1, "\ttype : %s\n", out_get_pool_type_str(params->type));
279 outv(1, "\tsize : %s\n", out_get_size_str(params->size, 2));
280 outv(1, "\tmode : 0%o\n", params->mode);
281 switch (params->type) {
282 case PMEM_POOL_TYPE_BLK:
283 outv(1, "\tbsize : %s\n",
284 out_get_size_str(params->blk.bsize, 0));
285 break;
286 case PMEM_POOL_TYPE_OBJ:
287 outv(1, "\tlayout: '%s'\n", params->obj.layout);
288 break;
289 default:
290 break;
291 }
292 }
293
294 /*
295 * inherit_pool_params -- inherit pool parameters from specified file
296 */
297 static int
298 inherit_pool_params(struct pmempool_create *pcp)
299 {
300 outv(1, "Parsing pool: '%s'\n", pcp->inherit_fname);
301
302 /*
303 * If no type string passed, --inherit option must be passed
304 * so parse file and get required parameters.
305 */
306 if (pmem_pool_parse_params(pcp->inherit_fname,
307 &pcp->inherit_params, 1)) {
308 if (errno)
309 perror(pcp->inherit_fname);
310 else
311 outv_err("%s: cannot determine type of pool\n",
312 pcp->inherit_fname);
313 return -1;
314 }
315
316 if (PMEM_POOL_TYPE_UNKNOWN == pcp->inherit_params.type) {
317 outv_err("'%s' -- unknown pool type\n",
318 pcp->inherit_fname);
319 return -1;
320 }
321
322 print_pool_params(&pcp->inherit_params);
323
324 return 0;
325 }
326
327 /*
328 * pmempool_create_parse_args -- parse command line args
329 */
330 static int
331 pmempool_create_parse_args(struct pmempool_create *pcp, const char *appname,
332 int argc, char *argv[], struct options *opts)
333 {
334 int opt, ret;
335 while ((opt = util_options_getopt(argc, argv, "vhi:s:Mm:l:wfb",
336 opts)) != -1) {
337 switch (opt) {
338 case 'v':
339 pcp->verbose = 1;
340 break;
341 case 'h':
342 pmempool_create_help(appname);
343 exit(EXIT_SUCCESS);
344 case 's':
345 pcp->str_size = optarg;
346 ret = util_parse_size(optarg,
347 (size_t *)&pcp->params.size);
348 if (ret || pcp->params.size == 0) {
349 outv_err("invalid size value specified '%s'\n",
350 optarg);
351 return -1;
352 }
353 break;
354 case 'M':
355 pcp->max_size = 1;
356 break;
357 case 'm':
358 pcp->str_mode = optarg;
359 if (util_parse_mode(optarg, &pcp->params.mode)) {
360 outv_err("invalid mode value specified '%s'\n",
361 optarg);
362 return -1;
363 }
364 break;
365 case 'i':
366 pcp->inherit_fname = optarg;
367 break;
368 case 'w':
369 pcp->write_btt_layout = 1;
370 break;
371 case 'l':
372 pcp->layout = optarg;
373 break;
374 case 'f':
375 pcp->force = 1;
376 break;
377 case 'b':
378 pcp->clearbadblocks = 1;
379 break;
380 default:
381 print_usage(appname);
382 return -1;
383 }
384 }
385
386 /* check for <type>, <bsize> and <file> strings */
387 if (optind + 2 < argc) {
388 pcp->str_type = argv[optind];
389 pcp->str_bsize = argv[optind + 1];
390 pcp->fname = argv[optind + 2];
391 } else if (optind + 1 < argc) {
392 pcp->str_type = argv[optind];
393 pcp->fname = argv[optind + 1];
394 } else if (optind < argc) {
395 pcp->fname = argv[optind];
396 pcp->str_type = NULL;
397 } else {
398 print_usage(appname);
399 return -1;
400 }
401
402 return 0;
403 }
404
405 static int
406 allocate_max_size_available_file(const char *name_of_file, mode_t mode,
407 os_off_t max_size)
408 {
409 int fd = os_open(name_of_file, O_CREAT | O_EXCL | O_RDWR, mode);
410 if (fd == -1) {
411 outv_err("!open '%s' failed", name_of_file);
412 return -1;
413 }
414
415 os_off_t offset = 0;
416 os_off_t length = max_size - (max_size % (os_off_t)Pagesize);
417 int ret;
418 do {
419 ret = os_posix_fallocate(fd, offset, length);
420 if (ret == 0)
421 offset += length;
422 else if (ret != ENOSPC) {
423 os_close(fd);
424 if (os_unlink(name_of_file) == -1)
425 outv_err("!unlink '%s' failed", name_of_file);
426 errno = ret;
427 outv_err("!space allocation for '%s' failed",
428 name_of_file);
429 return -1;
430 }
431
432 length /= 2;
433 length -= (length % (os_off_t)Pagesize);
434 } while (length > (os_off_t)Pagesize);
435
436 os_close(fd);
437
438 return 0;
439 }
440
441 /*
442 * pmempool_create_func -- main function for create command
443 */
444 int
445 pmempool_create_func(const char *appname, int argc, char *argv[])
446 {
447 int ret = 0;
448 struct pmempool_create pc = pmempool_create_default;
449 pc.opts = util_options_alloc(long_options, sizeof(long_options) /
450 sizeof(long_options[0]), NULL);
451
452 /* parse command line arguments */
453 ret = pmempool_create_parse_args(&pc, appname, argc, argv, pc.opts);
454 if (ret)
455 exit(EXIT_FAILURE);
456
457 /* set verbosity level */
458 out_set_vlevel(pc.verbose);
459
460 umask(0);
461
462 int exists = util_file_exists(pc.fname);
463 if (exists < 0)
464 return -1;
465
466 pc.fexists = exists;
467 int is_poolset = util_is_poolset_file(pc.fname) == 1;
468
469 if (pc.inherit_fname) {
470 if (inherit_pool_params(&pc)) {
471 outv_err("parsing pool '%s' failed\n",
472 pc.inherit_fname);
473 return -1;
474 }
475 }
476
477 /*
478 * Parse pool type and other parameters if --inherit option
479 * passed. It is possible to either pass --inherit option
480 * or pool type string in command line arguments. This is
481 * validated here.
482 */
483 if (pc.str_type) {
484 /* parse pool type string if passed in command line arguments */
485 pc.params.type = pmem_pool_type_parse_str(pc.str_type);
486 if (PMEM_POOL_TYPE_UNKNOWN == pc.params.type) {
487 outv_err("'%s' -- unknown pool type\n", pc.str_type);
488 return -1;
489 }
490
491 if (PMEM_POOL_TYPE_BLK == pc.params.type) {
492 if (pc.str_bsize == NULL) {
493 outv_err("blk pool requires <bsize> "
494 "argument\n");
495 return -1;
496 }
497 if (util_parse_size(pc.str_bsize,
498 (size_t *)&pc.params.blk.bsize)) {
499 outv_err("cannot parse '%s' as block size\n",
500 pc.str_bsize);
501 return -1;
502 }
503 }
504
505 if (PMEM_POOL_TYPE_OBJ == pc.params.type && pc.layout != NULL) {
506 size_t max_layout = PMEMOBJ_MAX_LAYOUT;
507
508 if (strlen(pc.layout) >= max_layout) {
509 outv_err(
510 "Layout name is too long, maximum number of characters (including the terminating null byte) is %zu\n",
511 max_layout);
512 return -1;
513 }
514
515 size_t len = sizeof(pc.params.obj.layout);
516 strncpy(pc.params.obj.layout, pc.layout, len);
517 pc.params.obj.layout[len - 1] = '\0';
518 }
519 } else if (pc.inherit_fname) {
520 pc.params.type = pc.inherit_params.type;
521 } else {
522 /* neither pool type string nor --inherit options passed */
523 print_usage(appname);
524 return -1;
525 }
526
527 if (util_options_verify(pc.opts, pc.params.type))
528 return -1;
529
530 if (pc.params.type != PMEM_POOL_TYPE_BLK && pc.str_bsize != NULL) {
531 outv_err("invalid option specified for %s pool type"
532 " -- block size\n",
533 out_get_pool_type_str(pc.params.type));
534 return -1;
535 }
536
537 if (is_poolset) {
538 if (pc.params.size) {
539 outv_err("-s|--size cannot be used with "
540 "poolset file\n");
541 return -1;
542 }
543
544 if (pc.max_size) {
545 outv_err("-M|--max-size cannot be used with "
546 "poolset file\n");
547 return -1;
548 }
549 }
550
551 if (pc.params.size && pc.max_size) {
552 outv_err("-M|--max-size option cannot be used with -s|--size"
553 " option\n");
554 return -1;
555 }
556
557 if (pc.inherit_fname) {
558 if (!pc.str_size && !pc.max_size)
559 pc.params.size = pc.inherit_params.size;
560 if (!pc.str_mode)
561 pc.params.mode = pc.inherit_params.mode;
562 switch (pc.params.type) {
563 case PMEM_POOL_TYPE_BLK:
564 if (!pc.str_bsize)
565 pc.params.blk.bsize =
566 pc.inherit_params.blk.bsize;
567 break;
568 case PMEM_POOL_TYPE_OBJ:
569 if (!pc.layout) {
570 memcpy(pc.params.obj.layout,
571 pc.inherit_params.obj.layout,
572 sizeof(pc.params.obj.layout));
573 } else {
574 size_t len = sizeof(pc.params.obj.layout);
575 strncpy(pc.params.obj.layout, pc.layout,
576 len - 1);
577 pc.params.obj.layout[len - 1] = '\0';
578 }
579 break;
580 default:
581 break;
582 }
583 }
584
585 /*
586 * If neither --size nor --inherit options passed, check
587 * for --max-size option - if not passed use minimum pool size.
588 */
589 uint64_t min_size = pmem_pool_get_min_size(pc.params.type);
590 if (pc.params.size == 0) {
591 if (pc.max_size) {
592 outv(1, "Maximum size option passed "
593 "- getting available space of file system.\n");
594 ret = pmempool_get_max_size(pc.fname,
595 &pc.params.size);
596 if (ret) {
597 outv_err("cannot get available space of fs\n");
598 return -1;
599 }
600 if (pc.params.size == 0) {
601 outv_err("No space left on device\n");
602 return -1;
603 }
604 outv(1, "Available space is %s\n",
605 out_get_size_str(pc.params.size, 2));
606 if (allocate_max_size_available_file(pc.fname,
607 pc.params.mode,
608 (os_off_t)pc.params.size))
609 return -1;
610 /*
611 * We are going to create pool based
612 * on file size instead of the pc.params.size.
613 */
614 pc.params.size = 0;
615 } else {
616 if (!pc.fexists) {
617 outv(1, "No size option passed "
618 "- picking minimum pool size.\n");
619 pc.params.size = min_size;
620 }
621 }
622 } else {
623 if (pc.params.size < min_size) {
624 outv_err("size must be >= %lu bytes\n", min_size);
625 return -1;
626 }
627 }
628
629 if (pc.force)
630 pmempool_rm(pc.fname, PMEMPOOL_RM_FORCE);
631
632 outv(1, "Creating pool: %s\n", pc.fname);
633 print_pool_params(&pc.params);
634
635 if (pc.clearbadblocks) {
636 int ret = util_pool_clear_badblocks(pc.fname,
637 1 /* ignore non-existing */);
638 if (ret) {
639 outv_err("'%s' -- clearing bad blocks failed\n",
640 pc.fname);
641 return -1;
642 }
643 }
644
645 switch (pc.params.type) {
646 case PMEM_POOL_TYPE_BLK:
647 ret = pmempool_create_blk(&pc);
648 break;
649 case PMEM_POOL_TYPE_LOG:
650 ret = pmempool_create_log(&pc);
651 break;
652 case PMEM_POOL_TYPE_OBJ:
653 ret = pmempool_create_obj(&pc);
654 break;
655 default:
656 ret = -1;
657 break;
658 }
659
660 if (ret) {
661 outv_err("creating pool file failed\n");
662 if (!pc.fexists)
663 util_unlink(pc.fname);
664 }
665
666 util_options_free(pc.opts);
667 return ret;
668 }