Hello! 2018-08-13 8:54 GMT+02:00 Shaoxiong Li <dahefanteng@xxxxxxxxx>: > Arrange the code for a better user experience. > show the bcache devices in both table-like format and tree-like format. > users can use this tools to manage device(regist,unregist,attach,detach,set-cachemode). Most tools that use the "*ctl" interface that I know of don't have a dash in front of ctl. Do you think it's worth to remove the dash and call it "bcachectl"? Regards, Kai > --- > Makefile | 47 +-- > README | 30 +- > bcache-ctl.c | 603 ++++++++++++++++++++++++++++++++++++++ > bcache-super-show.8 | 11 - > bcache-super-show.c | 257 ----------------- > bcache.c | 2 +- > lib.c | 472 ++++++++++++++++++++++++++++++ > lib.h | 60 ++++ > list.h | 749 ++++++++++++++++++++++++++++++++++++++++++++++++ > make-bcache.c | 2 +- > make-bcache.h | 1 + > package/bcache-ctl.spec | 55 ++++ > package/rpmbuild.sh | 29 ++ > 13 files changed, 2024 insertions(+), 294 deletions(-) > create mode 100644 bcache-ctl.c > delete mode 100644 bcache-super-show.8 > delete mode 100644 bcache-super-show.c > create mode 100644 lib.c > create mode 100644 lib.h > create mode 100644 list.h > create mode 100644 make-bcache.h > create mode 100755 package/bcache-ctl.spec > create mode 100755 package/rpmbuild.sh > > diff --git a/Makefile b/Makefile > index c824ae3..9924691 100644 > --- a/Makefile > +++ b/Makefile > @@ -1,32 +1,37 @@ > - > +.PHONY: clean > PREFIX=/usr > UDEVLIBDIR=/lib/udev > DRACUTLIBDIR=/lib/dracut > INSTALL=install > -CFLAGS+=-O2 -Wall -g > > -all: make-bcache probe-bcache bcache-super-show bcache-register > +CFLAGS+=`pkg-config --cflags blkid uuid smartcols` > +LDFLAGS+=`pkg-config --libs blkid uuid smartcols` > > -install: make-bcache probe-bcache bcache-super-show > - $(INSTALL) -m0755 make-bcache bcache-super-show $(DESTDIR)${PREFIX}/sbin/ > - $(INSTALL) -m0755 probe-bcache bcache-register $(DESTDIR)$(UDEVLIBDIR)/ > - $(INSTALL) -m0644 69-bcache.rules $(DESTDIR)$(UDEVLIBDIR)/rules.d/ > - $(INSTALL) -m0644 -- *.8 $(DESTDIR)${PREFIX}/share/man/man8/ > - $(INSTALL) -D -m0755 initramfs/hook $(DESTDIR)/usr/share/initramfs-tools/hooks/bcache > - $(INSTALL) -D -m0755 initcpio/install $(DESTDIR)/usr/lib/initcpio/install/bcache > - $(INSTALL) -D -m0755 dracut/module-setup.sh $(DESTDIR)$(DRACUTLIBDIR)/modules.d/90bcache/module-setup.sh > -# $(INSTALL) -m0755 bcache-test $(DESTDIR)${PREFIX}/sbin/ > +all:depend bcache-ctl probe-bcache bcache-register > > -clean: > - $(RM) -f make-bcache probe-bcache bcache-super-show bcache-test -- *.o > > -bcache-test: LDLIBS += `pkg-config --libs openssl` -lm > -make-bcache: LDLIBS += `pkg-config --libs uuid blkid` > -make-bcache: CFLAGS += `pkg-config --cflags uuid blkid` > -make-bcache: bcache.o > +SRCS=bcache-ctl.c bcache.c lib.c make-bcache.c > +depend: .depend > +.depend: $(SRCS) > + rm -f ./.depend > + $(CC) $(CFLAGS) -MM $^ > ./.depend; > + > +include .depend > + > +bcache-ctl: bcache-ctl.o bcache.o lib.o make-bcache.o > probe-bcache: LDLIBS += `pkg-config --libs uuid blkid` > probe-bcache: CFLAGS += `pkg-config --cflags uuid blkid` > -bcache-super-show: LDLIBS += `pkg-config --libs uuid` > -bcache-super-show: CFLAGS += -std=gnu99 > -bcache-super-show: bcache.o > bcache-register: bcache-register.o > + > + > +install: > + $(INSTALL) -m0755 bcache-ctl $(DESTDIR)${PREFIX}/sbin/ > + $(INSTALL) -m0755 probe-bcache bcache-register $(DESTDIR)$(UDEVLIBDIR)/ > + $(INSTALL) -m0644 69-bcache.rules $(DESTDIR)$(UDEVLIBDIR)/rules.d/ > + $(INSTALL) -m0644 -- *.8 $(DESTDIR)${PREFIX}/share/man/man8/ > + $(INSTALL) -D -m0755 initramfs/hook $(DESTDIR)/usr/share/initramfs-tools/hooks/bcache > + $(INSTALL) -D -m0755 initcpio/install $(DESTDIR)/usr/lib/initcpio/install/bcache > + $(INSTALL) -D -m0755 dracut/module-setup.sh $(DESTDIR)$(DRACUTLIBDIR)/modules.d/90bcache/module-setup.sh > + > +clean: > + rm -f *.o .depend bcache-ctl probe-bcache bcache-register > diff --git a/README b/README > index 0232f70..9b50ca9 100644 > --- a/README > +++ b/README > @@ -7,7 +7,11 @@ Documentation/bcache.txt. > > Included: > > -make-bcache > +bcache-ctl > + > +bcache-ctl has a few subcommands > + > +bcache-ctl make > Formats a block device for use with bcache. A device can be formatted for use > as a cache or as a backing device (requires yet to be implemented kernel > support). The most important option is for specifying the bucket size. > @@ -17,8 +21,28 @@ performance. The bucket size is intended to be equal to the size of your SSD's > erase blocks, which seems to be 128k-512k for most SSDs; feel free to > experiment. > > -bcache-super-show > -Prints the bcache superblock of a cache device or a backing device. > +bcache-ctl show > +show overall information about all devices > +-d Prints the bcache superblock of a cache device or a backing device. > +-m show overall information about all devices with detail info > + > +bcache-ctl tree > +show active bcache devices and their relations in a tree-like format > + > +bcache-ctl regist > +regist device to the kernel > + > +bcache-ctl unregist > +unregist device from the kernel > + > +bcache-ctl attach > +attach backend device(data device) to cache device > + > +bcache-ctl detach > +detach backend device(data device) from cache device > + > +bcache-ctl set-cachemode > +set cachemode for backend device(data device) > > > Udev rules > diff --git a/bcache-ctl.c b/bcache-ctl.c > new file mode 100644 > index 0000000..364d093 > --- /dev/null > +++ b/bcache-ctl.c > @@ -0,0 +1,603 @@ > +#include <stdio.h> > +#include <inttypes.h> > +#include <string.h> > +#include <stdint.h> > +#include <stdlib.h> > +#include <stdbool.h> > +#include <unistd.h> > +#include <getopt.h> > +#include <regex.h> > +#include "make-bcache.h" > +#include "bcache.h" > +#include "lib.h" > +#include "libsmartcols/libsmartcols.h" > +#include <locale.h> > +#include "list.h" > + > + > +//utils function > +static bool accepted_char(char c) > +{ > + if ('0' <= c && c <= '9') > + return true; > + if ('A' <= c && c <= 'Z') > + return true; > + if ('a' <= c && c <= 'z') > + return true; > + if (strchr(".-_", c)) > + return true; > + return false; > +} > + > +static void print_encode(char *in) > +{ > + char *pos; > + for (pos = in; *pos; pos++) > + if (accepted_char(*pos)) > + putchar(*pos); > + else > + printf("%%%x", *pos); > +} > + > +bool bad_uuid(char *uuid) > +{ > + const char *pattern = > + "^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$"; > + regex_t reg; > + int status; > + regmatch_t regmatche; > + if (regcomp(®, pattern, REG_EXTENDED) != 0) { > + fprintf(stderr, "Error happen when check uuid format:%m"); > + } > + status = regexec(®, uuid, 1, ®matche, 0); > + regfree(®); > + if (status == REG_NOMATCH) { > + return true; > + } else { > + return false; > + } > +} > + > +bool bad_dev(char *devname) > +{ > + const char *pattern = "^/dev/[a-zA-Z0-9]*$"; > + regex_t reg; > + int status; > + regmatch_t regmatche; > + if (regcomp(®, pattern, REG_EXTENDED) != 0) { > + fprintf(stderr, > + "Error happen when check device name format:%m"); > + } > + status = regexec(®, devname, 1, ®matche, 0); > + regfree(®); > + if (status == REG_NOMATCH) { > + return true; > + } else { > + return false; > + } > +} > + > + > +int ctlusage() > +{ > + fprintf(stderr, > + "Usage:bcache-ctl [SUBCMD]\n" > + " show show all bcache devices in this host\n" > + " tree show active bcache devices in this host\n" > + " make make regular device to bcache device\n" > + " regist regist device to kernel\n" > + " unregist unregist device from kernel\n" > + " attach attach backend device(data device) to cache device\n" > + " detach detach backend device(data device) from cache device\n" > + " set-cachemode set cachemode for backend device\n"); > + return EXIT_FAILURE; > +} > + > +int showusage() > +{ > + fprintf(stderr, > + "Usage: show [option]" > + " show overall information about all devices\n" > + " -d --device {devname} show the detail infomation about this device\n" > + " -m --more show overall information about all devices with detail info \n" > + " -h --help show help information \n"); > + return EXIT_FAILURE; > +} > + > +int treeusage() > +{ > + fprintf(stderr, > + "Usage: tree show active bcache devices in this host\n"); > + return EXIT_FAILURE; > +} > + > +int registusage() > +{ > + fprintf(stderr, > + "Usage:regist devicename regist device as bcache device to kernel\n"); > + return EXIT_FAILURE; > +} > + > +int unregistusage() > +{ > + fprintf(stderr, > + "Usage:unregist devicename unregist device from kernel\n"); > + return EXIT_FAILURE; > +} > + > +int attachusage() > +{ > + fprintf(stderr, "Usage:attach cset_uuid|cachedevice datadevice\n"); > + return EXIT_FAILURE; > +} > + > +int detachusage() > +{ > + fprintf(stderr, "Usage:detach devicename\n"); > + return EXIT_FAILURE; > +} > + > +int setcachemodeusage() > +{ > + fprintf(stderr, "Usage:set-cachemode devicename modetype\n"); > + return EXIT_FAILURE; > +} > + > + > +void free_dev(struct list_head *head) > +{ > + struct dev *dev; > + list_for_each_entry(dev, head, dev_list) { > + free(dev); > + } > +} > + > +int show_bdevs_detail() > +{ > + struct list_head head; > + struct dev *devs; > + INIT_LIST_HEAD(&head); > + int ret; > + > + ret = list_bdevs(&head); > + if (ret != 0) { > + fprintf(stderr, "Failed to list devices\n"); > + return ret; > + } > + printf > + ("Name\t\tUuid\t\t\t\t\tCset_Uuid\t\t\t\tType\t\tState\t\tBname\t\tAttachToDev\tAttachToCset\n"); > + char state[20]; > + list_for_each_entry(devs, &head, dev_list) { > + printf("%s\t%s\t%s\t%d", devs->name, devs->uuid, > + devs->cset, devs->version); > + switch (devs->version) { > + // These are handled the same by the kernel > + case BCACHE_SB_VERSION_CDEV: > + case BCACHE_SB_VERSION_CDEV_WITH_UUID: > + printf(" (cache)"); > + break; > + > + // The second adds data offset supporet > + case BCACHE_SB_VERSION_BDEV: > + case BCACHE_SB_VERSION_BDEV_WITH_OFFSET: > + printf(" (data)"); > + break; > + > + default: > + printf(" (unknown)"); > + break; > + } > + > + printf("\t%-8s", devs->state); > + printf("\t%-16s", devs->bname); > + > + char attachdev[30]; > + if (strlen(devs->attachuuid) == 36) { > + cset_to_devname(&head, devs->cset, attachdev); > + } else if (devs->version == BCACHE_SB_VERSION_CDEV > + || devs->version == > + BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + strcpy(attachdev, BCACHE_NO_SUPPORT); > + } else { > + strcpy(attachdev, BCACHE_ATTACH_ALONE); > + } > + printf("%-16s", attachdev); > + > + printf("%s", devs->attachuuid); > + putchar('\n'); > + } > + free_dev(&head); > + return 0; > +} > + > + > +int show_bdevs() > +{ > + struct list_head head; > + struct dev *devs; > + INIT_LIST_HEAD(&head); > + int ret; > + ret = list_bdevs(&head); > + if (ret != 0) { > + fprintf(stderr, "Failed to list devices\n"); > + return ret; > + } > + > + printf("Name\t\tType\t\tState\t\tBname\t\tAttachToDev\n"); > + char state[20]; > + list_for_each_entry(devs, &head, dev_list) { > + printf("%s\t%d", devs->name, devs->version); > + switch (devs->version) { > + // These are handled the same by the kernel > + case BCACHE_SB_VERSION_CDEV: > + case BCACHE_SB_VERSION_CDEV_WITH_UUID: > + printf(" (cache)"); > + break; > + > + // The second adds data offset supporet > + case BCACHE_SB_VERSION_BDEV: > + case BCACHE_SB_VERSION_BDEV_WITH_OFFSET: > + printf(" (data)"); > + break; > + > + default: > + printf(" (unknown)"); > + break; > + } > + > + printf("\t%-8s", devs->state); > + printf("\t%-16s", devs->bname); > + > + char attachdev[30]; > + if (strlen(devs->attachuuid) == 36) { > + cset_to_devname(&head, devs->cset, attachdev); > + } else if (devs->version == BCACHE_SB_VERSION_CDEV > + || devs->version == > + BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + strcpy(attachdev, BCACHE_NO_SUPPORT); > + } else { > + strcpy(attachdev, BCACHE_ATTACH_ALONE); > + } > + printf("%s", attachdev); > + putchar('\n'); > + } > + free_dev(&head); > + return 0; > +} > + > +int detail(char *devname) > +{ > + struct bdev bd; > + struct cdev cd; > + int type = 1; > + int ret; > + ret = detail_dev(devname, &bd, &cd, &type); > + if (ret != 0) { > + fprintf(stderr, "Failed to detail device\n", devname); > + return ret; > + } > + if (type == BCACHE_SB_VERSION_BDEV) { > + printf("sb.magic\t\t%s\n", bd.base.magic); > + printf("sb.first_sector\t\t%" PRIu64 "\n", > + bd.base.first_sector); > + printf("sb.csum\t\t\t%" PRIX64 "\n", bd.base.csum); > + printf("sb.version\t\t%" PRIu64, bd.base.version); > + printf(" [backing device]\n"); > + putchar('\n'); > + printf("dev.label\t\t"); > + if (*bd.base.label) { > + print_encode(bd.base.label); > + } else { > + printf("(empty)"); > + } > + putchar('\n'); > + printf("dev.uuid\t\t%s\n", bd.base.uuid); > + printf("dev.sectors_per_block\t%u\n" > + "dev.sectors_per_bucket\t%u\n", > + bd.base.sectors_per_block, > + bd.base.sectors_per_bucket); > + printf("dev.data.first_sector\t%ju\n" > + "dev.data.cache_mode\t%ju", > + bd.first_sector, bd.cache_mode); > + switch (bd.cache_mode) { > + case CACHE_MODE_WRITETHROUGH: > + printf(" [writethrough]\n"); > + break; > + case CACHE_MODE_WRITEBACK: > + printf(" [writeback]\n"); > + break; > + case CACHE_MODE_WRITEAROUND: > + printf(" [writearound]\n"); > + break; > + case CACHE_MODE_NONE: > + printf(" [no caching]\n"); > + break; > + default: > + putchar('\n'); > + } > + printf("dev.data.cache_state\t%ju", bd.cache_state); > + switch (bd.cache_state) { > + case BDEV_STATE_NONE: > + printf(" [detached]\n"); > + break; > + case BDEV_STATE_CLEAN: > + printf(" [clean]\n"); > + break; > + case BDEV_STATE_DIRTY: > + printf(" [dirty]\n"); > + break; > + case BDEV_STATE_STALE: > + printf(" [inconsistent]\n"); > + break; > + default: > + putchar('\n'); > + } > + > + putchar('\n'); > + printf("cset.uuid\t\t%s\n", bd.base.cset); > + } else if (type == BCACHE_SB_VERSION_CDEV > + || type == BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + printf("sb.magic\t\t%s\n", cd.base.magic); > + printf("sb.first_sector\t\t%" PRIu64 "\n", > + cd.base.first_sector); > + printf("sb.csum\t\t\t%" PRIX64 "\n", cd.base.csum); > + printf("sb.version\t\t%" PRIu64, cd.base.version); > + printf(" [cache device]\n"); > + putchar('\n'); > + printf("dev.label\t\t"); > + if (*cd.base.label) { > + print_encode(cd.base.label); > + } else { > + printf("(empty)"); > + } > + putchar('\n'); > + printf("dev.uuid\t\t%s\n", cd.base.uuid); > + printf("dev.sectors_per_block\t%u\n" > + "dev.sectors_per_bucket\t%u\n", > + cd.base.sectors_per_block, > + cd.base.sectors_per_bucket); > + printf("dev.cache.first_sector\t%u\n" > + "dev.cache.cache_sectors\t%ju\n" > + "dev.cache.total_sectors\t%ju\n" > + "dev.cache.ordered\t%s\n" > + "dev.cache.discard\t%s\n" > + "dev.cache.pos\t\t%u\n" > + "dev.cache.replacement\t%d", > + cd.first_sector, > + cd.cache_sectors, > + cd.total_sectors, > + cd.ordered ? "yes" : "no", > + cd.discard ? "yes" : "no", cd.pos, cd.replacement); > + switch (cd.replacement) { > + case CACHE_REPLACEMENT_LRU: > + printf(" [lru]\n"); > + break; > + case CACHE_REPLACEMENT_FIFO: > + printf(" [fifo]\n"); > + break; > + case CACHE_REPLACEMENT_RANDOM: > + printf(" [random]\n"); > + break; > + default: > + putchar('\n'); > + } > + > + putchar('\n'); > + printf("cset.uuid\t\t%s\n", cd.base.cset); > + } else { > + printf("type is %d", type); > + } > +} > + > +int tree() > +{ > + struct list_head head; > + struct dev *devs, *tmp; > + INIT_LIST_HEAD(&head); > + int ret; > + ret = list_bdevs(&head); > + if (ret != 0) { > + fprintf(stderr, "Failed to list devices\n"); > + return ret; > + } > + struct libscols_table *tb; > + struct libscols_line *dad, *son; > + enum { COL_CSET, COL_BNAME }; > + setlocale(LC_ALL, ""); > + tb = scols_new_table(); > + scols_table_new_column(tb, ".", 0.1, SCOLS_FL_TREE); > + scols_table_new_column(tb, "", 2, SCOLS_FL_TRUNC); > + list_for_each_entry(devs, &head, dev_list) { > + if ((devs->version == BCACHE_SB_VERSION_CDEV > + || devs->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) > + && strcmp(devs->state, BCACHE_BASIC_STATE_ACTIVE) == 0) { > + dad = scols_table_new_line(tb, NULL); > + scols_line_set_data(dad, COL_CSET, devs->name); > + list_for_each_entry(tmp, &head, dev_list) { > + if (strcmp(devs->cset, tmp->attachuuid) == > + 0) { > + son = > + scols_table_new_line(tb, dad); > + scols_line_set_data(son, COL_CSET, > + tmp->name); > + scols_line_set_data(son, COL_BNAME, > + tmp->bname); > + } > + } > + } > + } > + scols_print_table(tb); > + scols_unref_table(tb); > + free_dev(&head); > +} > + > +int attach_both(char *cdev, char *backdev) > +{ > + struct bdev bd; > + struct cdev cd; > + int type = 1; > + int ret; > + char buf[100]; > + ret = detail_dev(backdev, &bd, &cd, &type); > + if (ret < 0) { > + return ret; > + } > + if (type != BCACHE_SB_VERSION_BDEV > + && type != BCACHE_SB_VERSION_BDEV_WITH_OFFSET) { > + fprintf(stderr, "%s is not an backend device\n", backdev); > + return 1; > + } > + if (strcmp(bd.base.attachuuid, BCACHE_BNAME_NOT_EXIST) != 0) { > + fprintf(stderr, > + "This device have attached to another cset\n"); > + return 1; > + } > + > + if (strlen(cdev) != 36) { > + ret = detail_dev(cdev, &bd, &cd, &type); > + if (type != BCACHE_SB_VERSION_CDEV > + && type != BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + fprintf(stderr, "%s is not an cache device", cdev); > + return 1; > + } > + strcpy(buf, cd.base.cset); > + } else { > + strcpy(buf, cdev); > + } > + return attach(buf, backdev); > +} > + > +int main(int argc, char **argv) > +{ > + char *subcmd; > + if (argc < 2) { > + ctlusage(); > + return 1; > + } else { > + subcmd = argv[1]; > + argc--; > + argv += 1; > + } > + > + if (strcmp(subcmd, "make") == 0) { > + return make_bcache(argc, argv); > + > + } else if (strcmp(subcmd, "show") == 0) { > + int ret; > + int o = 0; > + char *devname; > + int more = 0; > + int device = 0; > + int help = 0; > + > + static struct option long_options[] = { > + {"more", no_argument, 0, 'm'}, > + {"help", no_argument, 0, 'h'}, > + {"device", required_argument, 0, 'd'}, > + {0, 0, 0, 0} > + }; > + int option_index = 0; > + while ((o = > + getopt_long(argc, argv, "hmd:", long_options, > + &option_index)) != EOF) { > + switch (o) { > + case 'd': > + devname = optarg; > + device = 1; > + break; > + case 'm': > + more = 1; > + break; > + case 'h': > + help = 1; > + break; > + case '?': > + return 1; > + } > + } > + argc -= optind; > + if (help || argc != 0) { > + return showusage(); > + } else if (more) { > + return show_bdevs_detail(); > + } else if (device) { > + if (bad_dev(devname)) { > + fprintf(stderr, > + "Error:Wrong device name found\n"); > + return 1; > + } > + return detail(devname); > + } else { > + return show_bdevs(); > + } > + } else if (strcmp(subcmd, "tree") == 0) { > + if (argc != 1) { > + return treeusage(); > + } > + return tree(); > + } else if (strcmp(subcmd, "regist") == 0) { > + if (argc != 2 || strcmp(argv[1], "-h") == 0) { > + return registusage(); > + } > + if (bad_dev(argv[1])) { > + fprintf(stderr, "Error:Wrong device name found\n"); > + return 1; > + } > + return regist(argv[1]); > + } else if (strcmp(subcmd, "unregist") == 0) { > + if (argc != 2 || strcmp(argv[1], "-h") == 0) { > + return unregistusage(); > + } > + if (bad_dev(argv[1])) { > + fprintf(stderr, "Error:Wrong device name found\n"); > + return 1; > + } > + struct bdev bd; > + struct cdev cd; > + int type = 1; > + int ret; > + ret = detail_dev(argv[1], &bd, &cd, &type); > + if (ret != 0) { > + return ret; > + } > + if (type == BCACHE_SB_VERSION_BDEV) { > + return stop_backdev(argv[1]); > + } else if (type == BCACHE_SB_VERSION_CDEV > + || type == BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + return unregist_cset(cd.base.cset); > + } > + return 1; > + } else if (strcmp(subcmd, "attach") == 0) { > + if (argc != 3 || strcmp(argv[1], "-h") == 0) { > + return attachusage(); > + } > + if (bad_dev(argv[1]) && bad_uuid(argv[1]) > + || bad_dev(argv[2])) { > + fprintf(stderr, > + "Error:Wrong device name or cache_set uuid found\n"); > + return 1; > + } > + return attach_both(argv[1], argv[2]); > + } else if (strcmp(subcmd, "detach") == 0) { > + if (argc != 2 || strcmp(argv[1], "-h") == 0) { > + return detachusage(); > + } > + if (bad_dev(argv[1])) { > + fprintf(stderr, "Error:Wrong device name found\n"); > + return 1; > + } > + return detach(argv[1]); > + } else if (strcmp(subcmd, "set-cachemode") == 0) { > + if (argc != 3) { > + return setcachemodeusage(); > + } > + if (bad_dev(argv[1])) { > + fprintf(stderr, "Error:Wrong device name found\n"); > + return 1; > + } > + return set_backdev_cachemode(argv[1], argv[2]); > + } else { > + ctlusage(); > + } > + return 0; > +} > diff --git a/bcache-super-show.8 b/bcache-super-show.8 > deleted file mode 100644 > index 7d15a93..0000000 > --- a/bcache-super-show.8 > +++ /dev/null > @@ -1,11 +0,0 @@ > -.TH bcache-super-show 8 > -.SH NAME > -bcache-super-show \- Print the bcache superblock > -.SH SYNOPSIS > -.B bcache-super-show > -[\fB \-f] > -.I device > -.SH OPTIONS > -.TP > -.BR \-f > -Keep going if the superblock crc is invalid > diff --git a/bcache-super-show.c b/bcache-super-show.c > deleted file mode 100644 > index 26cc40e..0000000 > --- a/bcache-super-show.c > +++ /dev/null > @@ -1,257 +0,0 @@ > -/* > - * Author: Gabriel de Perthuis <g2p.code@xxxxxxxxx> > - * > - * GPLv2 > - */ > - > - > -#define _FILE_OFFSET_BITS 64 > -#define __USE_FILE_OFFSET64 > -#define _XOPEN_SOURCE 500 > - > -#include <errno.h> > -#include <fcntl.h> > -#include <inttypes.h> > -#include <linux/fs.h> > -#include <stdbool.h> > -#include <stdint.h> > -#include <stdio.h> > -#include <stdlib.h> > -#include <string.h> > -#include <sys/ioctl.h> > -#include <sys/types.h> > -#include <sys/stat.h> > -#include <unistd.h> > -#include <uuid/uuid.h> > - > -#include "bcache.h" > - > - > -static void usage() > -{ > - fprintf(stderr, "Usage: bcache-super-show [-f] <device>\n"); > -} > - > - > -static bool accepted_char(char c) > -{ > - if ('0' <= c && c <= '9') > - return true; > - if ('A' <= c && c <= 'Z') > - return true; > - if ('a' <= c && c <= 'z') > - return true; > - if (strchr(".-_", c)) > - return true; > - return false; > -} > - > -static void print_encode(char* in) > -{ > - for (char* pos = in; *pos; pos++) > - if (accepted_char(*pos)) > - putchar(*pos); > - else > - printf("%%%x", *pos); > -} > - > - > -int main(int argc, char **argv) > -{ > - bool force_csum = false; > - int o; > - extern char *optarg; > - struct cache_sb sb; > - char uuid[40]; > - uint64_t expected_csum; > - > - while ((o = getopt(argc, argv, "f")) != EOF) > - switch (o) { > - case 'f': > - force_csum = 1; > - break; > - > - default: > - usage(); > - exit(1); > - } > - > - argv += optind; > - argc -= optind; > - > - if (argc != 1) { > - usage(); > - exit(1); > - } > - > - int fd = open(argv[0], O_RDONLY); > - if (fd < 0) { > - printf("Can't open dev %s: %s\n", argv[0], strerror(errno)); > - exit(2); > - } > - > - if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) { > - fprintf(stderr, "Couldn't read\n"); > - exit(2); > - } > - > - printf("sb.magic\t\t"); > - if (!memcmp(sb.magic, bcache_magic, 16)) { > - printf("ok\n"); > - } else { > - printf("bad magic\n"); > - fprintf(stderr, "Invalid superblock (bad magic)\n"); > - exit(2); > - } > - > - printf("sb.first_sector\t\t%" PRIu64, sb.offset); > - if (sb.offset == SB_SECTOR) { > - printf(" [match]\n"); > - } else { > - printf(" [expected %ds]\n", SB_SECTOR); > - fprintf(stderr, "Invalid superblock (bad sector)\n"); > - exit(2); > - } > - > - printf("sb.csum\t\t\t%" PRIX64, sb.csum); > - expected_csum = csum_set(&sb); > - if (sb.csum == expected_csum) { > - printf(" [match]\n"); > - } else { > - printf(" [expected %" PRIX64 "]\n", expected_csum); > - if (!force_csum) { > - fprintf(stderr, "Corrupt superblock (bad csum)\n"); > - exit(2); > - } > - } > - > - printf("sb.version\t\t%" PRIu64, sb.version); > - switch (sb.version) { > - // These are handled the same by the kernel > - case BCACHE_SB_VERSION_CDEV: > - case BCACHE_SB_VERSION_CDEV_WITH_UUID: > - printf(" [cache device]\n"); > - break; > - > - // The second adds data offset support > - case BCACHE_SB_VERSION_BDEV: > - case BCACHE_SB_VERSION_BDEV_WITH_OFFSET: > - printf(" [backing device]\n"); > - break; > - > - default: > - printf(" [unknown]\n"); > - // exit code? > - return 0; > - } > - > - putchar('\n'); > - > - char label[SB_LABEL_SIZE + 1]; > - strncpy(label, (char*)sb.label, SB_LABEL_SIZE); > - label[SB_LABEL_SIZE] = '\0'; > - printf("dev.label\t\t"); > - if (*label) > - print_encode(label); > - else > - printf("(empty)"); > - putchar('\n'); > - > - uuid_unparse(sb.uuid, uuid); > - printf("dev.uuid\t\t%s\n", uuid); > - > - printf("dev.sectors_per_block\t%u\n" > - "dev.sectors_per_bucket\t%u\n", > - sb.block_size, > - sb.bucket_size); > - > - if (!SB_IS_BDEV(&sb)) { > - // total_sectors includes the superblock; > - printf("dev.cache.first_sector\t%u\n" > - "dev.cache.cache_sectors\t%ju\n" > - "dev.cache.total_sectors\t%ju\n" > - "dev.cache.ordered\t%s\n" > - "dev.cache.discard\t%s\n" > - "dev.cache.pos\t\t%u\n" > - "dev.cache.replacement\t%ju", > - sb.bucket_size * sb.first_bucket, > - sb.bucket_size * (sb.nbuckets - sb.first_bucket), > - sb.bucket_size * sb.nbuckets, > - CACHE_SYNC(&sb) ? "yes" : "no", > - CACHE_DISCARD(&sb) ? "yes" : "no", > - sb.nr_this_dev, > - CACHE_REPLACEMENT(&sb)); > - switch (CACHE_REPLACEMENT(&sb)) { > - case CACHE_REPLACEMENT_LRU: > - printf(" [lru]\n"); > - break; > - case CACHE_REPLACEMENT_FIFO: > - printf(" [fifo]\n"); > - break; > - case CACHE_REPLACEMENT_RANDOM: > - printf(" [random]\n"); > - break; > - default: > - putchar('\n'); > - } > - > - } else { > - uint64_t first_sector; > - if (sb.version == BCACHE_SB_VERSION_BDEV) { > - first_sector = BDEV_DATA_START_DEFAULT; > - } else { > - if (sb.keys == 1 || sb.d[0]) { > - fprintf(stderr, > - "Possible experimental format detected, bailing\n"); > - exit(3); > - } > - first_sector = sb.data_offset; > - } > - > - printf("dev.data.first_sector\t%ju\n" > - "dev.data.cache_mode\t%ju", > - first_sector, > - BDEV_CACHE_MODE(&sb)); > - switch (BDEV_CACHE_MODE(&sb)) { > - case CACHE_MODE_WRITETHROUGH: > - printf(" [writethrough]\n"); > - break; > - case CACHE_MODE_WRITEBACK: > - printf(" [writeback]\n"); > - break; > - case CACHE_MODE_WRITEAROUND: > - printf(" [writearound]\n"); > - break; > - case CACHE_MODE_NONE: > - printf(" [no caching]\n"); > - break; > - default: > - putchar('\n'); > - } > - > - printf("dev.data.cache_state\t%ju", > - BDEV_STATE(&sb)); > - switch (BDEV_STATE(&sb)) { > - case BDEV_STATE_NONE: > - printf(" [detached]\n"); > - break; > - case BDEV_STATE_CLEAN: > - printf(" [clean]\n"); > - break; > - case BDEV_STATE_DIRTY: > - printf(" [dirty]\n"); > - break; > - case BDEV_STATE_STALE: > - printf(" [inconsistent]\n"); > - break; > - default: > - putchar('\n'); > - } > - } > - putchar('\n'); > - > - uuid_unparse(sb.set_uuid, uuid); > - printf("cset.uuid\t\t%s\n", uuid); > - > - return 0; > -} > diff --git a/bcache.c b/bcache.c > index 8f37445..8b4b986 100644 > --- a/bcache.c > +++ b/bcache.c > @@ -115,7 +115,7 @@ static const uint64_t crc_table[256] = { > 0x9AFCE626CE85B507ULL > }; > > -inline uint64_t crc64(const void *_data, size_t len) > +uint64_t crc64(const void *_data, size_t len) > { > uint64_t crc = 0xFFFFFFFFFFFFFFFFULL; > const unsigned char *data = _data; > diff --git a/lib.c b/lib.c > new file mode 100644 > index 0000000..0a27d3a > --- /dev/null > +++ b/lib.c > @@ -0,0 +1,472 @@ > +#include <stdbool.h> > +#include <blkid/blkid.h> > +#include <dirent.h> > +#include <sys/types.h> > +#include <unistd.h> > +#include <stdio.h> > +#include <fcntl.h> > +#include "bcache.h" > +#include "lib.h" > +#include <uuid/uuid.h> > +#include <string.h> > +#include <malloc.h> > + > + > +/* > + * utils function > + */ > + > +static void trim_prefix(char *dest, char *src, int num) > +{ > + strcpy(dest, src + num); > +} > + > +static void get_tail(char *dest, char *src, int n) > +{ > + int num, i; > + num = strlen(src); > + for (i = 0; i < n; i++) { > + dest[i] = src[num - n + i]; > + } > + dest[i] = '\0'; > +} > + > +static void trim_tail(char *src, int n) > +{ > + int num; > + num = strlen(src); > + src[num - n] = '\0'; > +} > + > + > +int get_backdev_state(char *devname, char *state) > +{ > + FILE *fd; > + char path[100]; > + char buf[40]; > + trim_prefix(buf, devname, DEV_PREFIX_LEN); > + sprintf(path, "/sys/block/%s/bcache/state", buf); > + fd = fopen(path, "r"); > + if (fd == NULL) { > + strcpy(state, BCACHE_BASIC_STATE_INACTIVE); > + return 0; > + } > + int i = 0; > + while ((state[i] = getc(fd)) != '\n') { > + i++; > + } > + state[i] = '\0'; > + fclose(fd); > + return 0; > +} > + > +int get_cachedev_state(char *cset_id, char *state) > +{ > + DIR *dir = NULL; > + char path[100]; > + sprintf(path, "/sys/fs/bcache/%s/", cset_id); > + dir = opendir(path); > + if (dir == NULL) { > + strcpy(state, BCACHE_BASIC_STATE_INACTIVE); > + } else { > + strcpy(state, BCACHE_BASIC_STATE_ACTIVE); > + } > + closedir(dir); > + return 0; > +} > + > +int get_state(struct dev *dev, char *state) > +{ > + if (dev->version == BCACHE_SB_VERSION_CDEV > + || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + return get_cachedev_state(dev->cset, state); > + } else if (dev->version == BCACHE_SB_VERSION_BDEV > + || dev->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET) { > + return get_backdev_state(dev->name, state); > + } > +} > + > + > +int get_dev_bname(char *devname, char *bname) > +{ > + int ret; > + char path[100]; > + char buf[40]; > + char link[100]; > + trim_prefix(buf, devname, DEV_PREFIX_LEN); > + sprintf(path, "/sys/block/%s/bcache/dev", buf); > + ret = readlink(path, link, sizeof(link)); > + if (ret < 0) { > + strcpy(bname, BCACHE_BNAME_NOT_EXIST); > + } else { > + trim_tail(link, strlen(link) - ret); > + strcpy(bname, link + 41); > + } > + return 0; > +} > + > +int get_bname(struct dev *dev, char *bname) > +{ > + if (dev->version == BCACHE_SB_VERSION_CDEV > + || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + strcpy(bname, BCACHE_NO_SUPPORT); > + } else if (dev->version == BCACHE_SB_VERSION_BDEV > + || dev->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET) { > + return get_dev_bname(dev->name, bname); > + } > + return 0; > +} > + > +int get_backdev_attachpoint(char *devname, char *point) > +{ > + int ret; > + char path[100]; > + char buf[20]; > + char link[100]; > + char uuid[40]; > + trim_prefix(buf, devname, DEV_PREFIX_LEN); > + sprintf(path, "/sys/block/%s/bcache/cache", buf); > + ret = readlink(path, link, sizeof(link)); > + if (ret < 0) { > + strcpy(point, BCACHE_BNAME_NOT_EXIST); > + } else { > + trim_tail(link, strlen(link) - ret); > + get_tail(uuid, link, 36); > + strcpy(point, uuid); > + } > + return 0; > +} > + > +int get_point(struct dev *dev, char *point) > +{ > + if (dev->version == BCACHE_SB_VERSION_CDEV > + || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + strcpy(point, BCACHE_NO_SUPPORT); > + } else if (dev->version == BCACHE_SB_VERSION_BDEV > + || dev->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET) { > + return get_backdev_attachpoint(dev->name, point); > + } > + return 0; > +} > + > +int cset_to_devname(struct list_head *head, char *cset, char *devname) > +{ > + struct dev *dev; > + list_for_each_entry(dev, head, dev_list) { > + if ((dev->version == BCACHE_SB_VERSION_CDEV > + || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) > + && strcmp(dev->cset, cset) == 0) { > + strcpy(devname, dev->name); > + } > + } > + return 0; > +} > + > + > +int detail_base(char *devname, struct cache_sb sb, struct dev *base) > +{ > + int ret; > + uint64_t expected_csum; > + strcpy(base->name, devname); > + base->magic = "ok"; > + base->first_sector = SB_SECTOR; > + base->csum = sb.csum; > + base->version = sb.version; > + > + strncpy(base->label, (char *) sb.label, SB_LABEL_SIZE); > + base->label[SB_LABEL_SIZE] = '\0'; > + > + uuid_unparse(sb.uuid, base->uuid); > + uuid_unparse(sb.set_uuid, base->cset); > + base->sectors_per_block = sb.block_size; > + base->sectors_per_bucket = sb.bucket_size; > + if ((ret = get_state(base, base->state)) != 0) { > + fprintf(stderr, "Failed to get state for %s\n", devname); > + return ret; > + } > + if ((ret = get_bname(base, base->bname)) != 0) { > + fprintf(stderr, "Failed to get bname for %s\n", devname); > + return ret; > + } > + if ((ret = get_point(base, base->attachuuid)) != 0) { > + fprintf(stderr, "Failed to get attachuuid for %s\n", > + devname); > + return ret; > + } > + return 0; > +} > + > +int list_bdevs(struct list_head *head) > +{ > + DIR *dir; > + struct dirent *ptr; > + blkid_probe pr; > + struct cache_sb sb; > + dir = opendir("/sys/block"); > + if (dir == NULL) { > + fprintf(stderr, "Unable to open dir /sys/block\n"); > + return 1; > + } > + while ((ptr = readdir(dir)) != NULL) { > + if (strcmp(ptr->d_name, ".") == 0 > + || strcmp(ptr->d_name, "..") == 0) { > + continue; > + } > + char dev[20]; > + sprintf(dev, "/dev/%s", ptr->d_name); > + int fd = open(dev, O_RDONLY); > + if (fd == -1) { > + continue; > + } > + > + if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) { > + close(fd); > + continue; > + } > + if (memcmp(sb.magic, bcache_magic, 16)) { > + close(fd); > + continue; > + } > + struct dev *tmp, *current; > + int ret; > + tmp = (struct dev *) malloc(DEVLEN); > + ret = detail_base(dev, sb, tmp); > + if (ret != 0) { > + fprintf(stderr, > + "Failed to get information for %s\n", dev); > + return 1; > + } else { > + list_add_tail(&tmp->dev_list, head); > + } > + } > + closedir(dir); > + return 0; > +} > + > +int detail_dev(char *devname, struct bdev *bd, struct cdev *cd, int *type) > +{ > + struct cache_sb sb; > + uint64_t expected_csum; > + int fd = open(devname, O_RDONLY); > + if (fd < 0) { > + fprintf(stderr, "Error: Can't open dev %s\n", devname); > + return 1; > + } > + > + if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) { > + fprintf(stderr, "Couldn't read\n"); > + goto Fail; > + } > + > + if (memcmp(sb.magic, bcache_magic, 16)) { > + fprintf(stderr, > + "Bad magic,make sure this is an bcache device\n"); > + goto Fail; > + } > + > + if (!sb.offset == SB_SECTOR) { > + fprintf(stderr, "Invalid superblock (bad sector)\n"); > + goto Fail; > + } > + > + expected_csum = csum_set(&sb); > + if (!sb.csum == expected_csum) { > + fprintf(stderr, "Csum is not match with expected one"); > + goto Fail; > + } > + > + *type = sb.version; > + if (sb.version == BCACHE_SB_VERSION_BDEV) { > + detail_base(devname, sb, &bd->base); > + bd->first_sector = BDEV_DATA_START_DEFAULT; > + bd->cache_mode = BDEV_CACHE_MODE(&sb); > + bd->cache_state = BDEV_STATE(&sb); > + } else if (sb.version == BCACHE_SB_VERSION_CDEV > + || sb.version == BCACHE_SB_VERSION_CDEV_WITH_UUID) { > + detail_base(devname, sb, &cd->base); > + cd->first_sector = sb.bucket_size * sb.first_bucket; > + cd->cache_sectors = > + sb.bucket_size * (sb.nbuckets - sb.first_bucket); > + cd->total_sectors = sb.bucket_size * sb.nbuckets; > + cd->ordered = CACHE_SYNC(&sb); > + cd->discard = CACHE_DISCARD(&sb); > + cd->pos = sb.nr_this_dev; > + cd->replacement = CACHE_REPLACEMENT(&sb); > + } else { > + fprintf(stderr, "Unknown bcache device type found"); > + goto Fail; > + } > + return 0; > + Fail: > + close(fd); > + return 1; > +} > + > +int regist(char *devname) > +{ > + int fd; > + fd = open("/sys/fs/bcache/register", O_WRONLY); > + if (fd < 0) { > + perror("Error opening /sys/fs/bcache/register"); > + fprintf(stderr, > + "The bcache kernel module must be loaded\n"); > + return 1; > + } > + if (dprintf(fd, "%s\n", devname) < 0) { > + fprintf(stderr, "Error registering %s with bcache: %m\n", > + devname); > + close(fd); > + return 1; > + } > + close(fd); > + return 0; > +} > + > +int unregist_cset(char *cset) > +{ > + int fd; > + char path[100]; > + sprintf(path, "/sys/fs/bcache/%s/unregister", cset); > + fd = open(path, O_WRONLY); > + if (fd < 0) { > + fprintf(stderr, "Can't open %s\n", path); > + return 1; > + } > + if (dprintf(fd, "%d\n", 1) < 0) { > + fprintf(stderr, "Failed to unregist this cache device"); > + close(fd); > + return 1; > + } > + close(fd); > + return 0; > +} > + > +int stop_backdev(char *devname) > +{ > + char path[100]; > + int fd; > + char buf[20]; > + trim_prefix(buf, devname, DEV_PREFIX_LEN); > + sprintf(path, "/sys/block/%s/bcache/stop", buf); > + fd = open(path, O_WRONLY); > + if (fd < 0) { > + fprintf(stderr, "Can't open %s\n", path); > + return 1; > + } > + if (dprintf(fd, "%s\n", "1") < 0) { > + fprintf(stderr, "Error stop back device %s\n", devname); > + close(fd); > + return 1; > + } > + close(fd); > + return 0; > +} > + > +int unregist_both(char *cset) > +{ > + int fd; > + char path[100]; > + sprintf(path, "/sys/fs/bcache/%s/stop", cset); > + fd = open(path, O_WRONLY); > + if (fd < 0) { > + fprintf(stderr, "Can't open %s\n", path); > + return 1; > + } > + if (dprintf(fd, "%d\n", 1) < 0) { > + fprintf(stderr, "Failed to stop cset and its backends %m"); > + close(fd); > + return 1; > + } > + close(fd); > + return 0; > +} > + > +int attach(char *cset, char *devname) > +{ > + int fd; > + char buf[20]; > + trim_prefix(buf, devname, DEV_PREFIX_LEN); > + char path[100]; > + sprintf(path, "/sys/block/%s/bcache/attach", buf); > + fd = open(path, O_WRONLY); > + if (fd < 0) { > + fprintf(stderr, "Can't open %s:%m\n", path); > + return 1; > + } > + if (dprintf(fd, "%s\n", cset) < 0) { > + fprintf(stderr, "Failed to attache to cset %s\n", cset); > + close(fd); > + return 1; > + } > + close(fd); > + return 0; > +} > + > +int detach(char *devname) > +{ > + int fd; > + char buf[20]; > + trim_prefix(buf, devname, DEV_PREFIX_LEN); > + char path[100]; > + sprintf(path, "/sys/block/%s/bcache/detach", buf); > + fd = open(path, O_WRONLY); > + if (fd < 0) { > + fprintf(stderr, > + "Can't open %s,Make sure the device name is correct\n", > + path); > + return 1; > + } > + if (dprintf(fd, "%d\n", 1) < 0) { > + close(fd); > + fprintf(stderr, "Error detach device %s:%m", devname); > + return 1; > + } > + close(fd); > + return 0; > +} > + > +int set_backdev_cachemode(char *devname, char *cachemode) > +{ > + int fd; > + char path[100]; > + char buf[20]; > + trim_prefix(buf, devname, DEV_PREFIX_LEN); > + sprintf(path, "/sys/block/%s/bcache/cache_mode", buf); > + fd = open(path, O_WRONLY); > + if (fd < 0) { > + fprintf(stderr, > + "Can't open %s,Make sure the device name is correct\n", > + path); > + return 1; > + } > + if (dprintf(fd, "%s\n", cachemode) < 0) { > + printf("Failed to set cachemode for device %s:%m\n", > + devname); > + close(fd); > + return 1; > + } > + close(fd); > + return 0; > +} > + > +int get_backdev_cachemode(char *devname, char *mode) > +{ > + int fd; > + char path[100]; > + sprintf(path, "/sys/block/%s/bcache/cache_mode", devname); > + fd = open(path, O_RDONLY); > + if (fd < 0) { > + perror("Error opening /sys/fs/bcache/register"); > + fprintf(stderr, > + "The bcache kernel module must be loaded\n"); > + return 1; > + } > + printf("size in func is %d", sizeof(mode)); > + if (read(fd, mode, 100) < 0) { > + fprintf(stderr, "Failed to fetch device cache mode\n"); > + close(fd); > + return 1; > + } > + close(fd); > + return 0; > +} > diff --git a/lib.h b/lib.h > new file mode 100644 > index 0000000..6911132 > --- /dev/null > +++ b/lib.h > @@ -0,0 +1,60 @@ > +#include "list.h" > + > +struct dev { > + char name[40]; > + char *magic; > + uint64_t first_sector; > + uint64_t csum; > + int version; > + char label[SB_LABEL_SIZE + 1]; > + char uuid[40]; > + int sectors_per_block; > + int sectors_per_bucket; > + char cset[40]; > + char state[40]; > + char bname[40]; > + char attachuuid[40]; > + struct list_head dev_list; > +}; > + > +struct bdev { > + struct dev base; > + int first_sector; > + int cache_mode; > + int cache_state; > +}; > + > +//typedef int bool; > +struct cdev { > + struct dev base; > + int first_sector; > + int cache_sectors; > + int total_sectors; > + bool ordered; > + bool discard; > + int pos; > + unsigned int replacement; > +}; > + > + > +int list_bdevs(struct list_head *head); > +int detail_dev(char *devname, struct bdev *bd, struct cdev *cd, int *type); > +int regist(char *devname); > +int stop_backdev(char *devname); > +int unregist_cset(char *cset); > +int attach(char *cset, char *devname); > +int detach(char *devname); > +int set_backdev_cachemode(char *devname, char *cachemode); > +int cset_to_devname(struct list_head *head, char *cset, char *devname); > + > + > +#define DEVLEN sizeof(struct dev) > + > +#define BCACHE_NO_SUPPORT "N/A" > + > +#define BCACHE_BASIC_STATE_ACTIVE "active" > +#define BCACHE_BASIC_STATE_INACTIVE "inactive" > + > +#define BCACHE_ATTACH_ALONE "Alone" > +#define BCACHE_BNAME_NOT_EXIST "Non-Exist" > +#define DEV_PREFIX_LEN 5 > diff --git a/list.h b/list.h > new file mode 100644 > index 0000000..c43f387 > --- /dev/null > +++ b/list.h > @@ -0,0 +1,749 @@ > +#ifndef _LINUX_LIST_H > +#define _LINUX_LIST_H > + > +#include <stdio.h> > + > +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) > + > +/** > + * * container_of - cast a member of a structure out to the containing structure > + * * @ptr: the pointer to the member. > + * * @type: the type of the container struct this is embedded in. > + * * @member: the name of the member within the struct. > + * * > + * */ > +#define container_of(ptr, type, member) ({ \ > + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ > + (type *)( (char *)__mptr - offsetof(type,member) );}) > + > +/* > + * * These are non-NULL pointers that will result in page faults > + * * under normal circumstances, used to verify that nobody uses > + * * non-initialized list entries. > + * */ > +#define LIST_POISON1 ((void *) 0x00100100) > +#define LIST_POISON2 ((void *) 0x00200200) > + > +struct list_head { > + struct list_head *next, *prev; > +}; > + > +struct hlist_head { > + struct hlist_node *first; > +}; > + > +struct hlist_node { > + struct hlist_node *next, **pprev; > +}; > + > +/* > + * * Simple doubly linked list implementation. > + * * > + * * Some of the internal functions ("__xxx") are useful when > + * * manipulating whole lists rather than single entries, as > + * * sometimes we already know the next/prev entries and we can > + * * generate better code by using them directly rather than > + * * using the generic single-entry routines. > + * */ > + > +#define LIST_HEAD_INIT(name) { &(name), &(name) } > + > +#define LIST_HEAD(name) \ > + struct list_head name = LIST_HEAD_INIT(name) > + > +static inline void INIT_LIST_HEAD(struct list_head *list) > +{ > + list->next = list; > + list->prev = list; > +} > + > +/* > + * * Insert a new entry between two known consecutive entries. > + * * > + * * This is only for internal list manipulation where we know > + * * the prev/next entries already! > + * */ > +#ifndef CONFIG_DEBUG_LIST > +static inline void __list_add(struct list_head *new, > + struct list_head *prev, > + struct list_head *next) > +{ > + next->prev = new; > + new->next = next; > + new->prev = prev; > + prev->next = new; > +} > +#else > +extern void __list_add(struct list_head *new, > + struct list_head *prev, > + struct list_head *next); > +#endif > + > +/** > + * * list_add - add a new entry > + * * @new: new entry to be added > + * * @head: list head to add it after > + * * > + * * Insert a new entry after the specified head. > + * * This is good for implementing stacks. > + * */ > +static inline void list_add(struct list_head *new, struct list_head *head) > +{ > + __list_add(new, head, head->next); > +} > + > + > +/** > + * * list_add_tail - add a new entry > + * * @new: new entry to be added > + * * @head: list head to add it before > + * * > + * * Insert a new entry before the specified head. > + * * This is useful for implementing queues. > + * */ > +static inline void list_add_tail(struct list_head *new, struct list_head *head) > +{ > + __list_add(new, head->prev, head); > +} > + > +/* > + * * Delete a list entry by making the prev/next entries > + * * point to each other. > + * * > + * * This is only for internal list manipulation where we know > + * * the prev/next entries already! > + * */ > +static inline void __list_del(struct list_head * prev, struct list_head * next) > +{ > + next->prev = prev; > + prev->next = next; > +} > + > +/** > + * * list_del - deletes entry from list. > + * * @entry: the element to delete from the list. > + * * Note: list_empty() on entry does not return true after this, the entry is > + * * in an undefined state. > + * */ > +#ifndef CONFIG_DEBUG_LIST > +static inline void __list_del_entry(struct list_head *entry) > +{ > + __list_del(entry->prev, entry->next); > +} > + > +static inline void list_del(struct list_head *entry) > +{ > + __list_del(entry->prev, entry->next); > + entry->next = LIST_POISON1; > + entry->prev = LIST_POISON2; > +} > +#else > +extern void __list_del_entry(struct list_head *entry); > +extern void list_del(struct list_head *entry); > +#endif > + > +/** > + * * list_replace - replace old entry by new one > + * * @old : the element to be replaced > + * * @new : the new element to insert > + * * > + * * If @old was empty, it will be overwritten. > + * */ > +static inline void list_replace(struct list_head *old, > + struct list_head *new) > +{ > + new->next = old->next; > + new->next->prev = new; > + new->prev = old->prev; > + new->prev->next = new; > +} > + > +static inline void list_replace_init(struct list_head *old, > + struct list_head *new) > +{ > + list_replace(old, new); > + INIT_LIST_HEAD(old); > +} > + > +/** > + * * list_del_init - deletes entry from list and reinitialize it. > + * * @entry: the element to delete from the list. > + * */ > +static inline void list_del_init(struct list_head *entry) > +{ > + __list_del_entry(entry); > + INIT_LIST_HEAD(entry); > +} > + > +/** > + * * list_move - delete from one list and add as another's head > + * * @list: the entry to move > + * * @head: the head that will precede our entry > + * */ > +static inline void list_move(struct list_head *list, struct list_head *head) > +{ > + __list_del_entry(list); > + list_add(list, head); > +} > + > +/** > + * * list_move_tail - delete from one list and add as another's tail > + * * @list: the entry to move > + * * @head: the head that will follow our entry > + * */ > +static inline void list_move_tail(struct list_head *list, > + struct list_head *head) > +{ > + __list_del_entry(list); > + list_add_tail(list, head); > +} > + > +/** > + * * list_is_last - tests whether @list is the last entry in list @head > + * * @list: the entry to test > + * * @head: the head of the list > + * */ > +static inline int list_is_last(const struct list_head *list, > + const struct list_head *head) > +{ > + return list->next == head; > +} > + > +/** > + * * list_empty - tests whether a list is empty > + * * @head: the list to test. > + * */ > +static inline int list_empty(const struct list_head *head) > +{ > + return head->next == head; > +} > + > +/** > + * * list_empty_careful - tests whether a list is empty and not being modified > + * * @head: the list to test > + * * > + * * Description: > + * * tests whether a list is empty _and_ checks that no other CPU might be > + * * in the process of modifying either member (next or prev) > + * * > + * * NOTE: using list_empty_careful() without synchronization > + * * can only be safe if the only activity that can happen > + * * to the list entry is list_del_init(). Eg. it cannot be used > + * * if another CPU could re-list_add() it. > + * */ > +static inline int list_empty_careful(const struct list_head *head) > +{ > + struct list_head *next = head->next; > + return (next == head) && (next == head->prev); > +} > + > +/** > + * * list_rotate_left - rotate the list to the left > + * * @head: the head of the list > + * */ > +static inline void list_rotate_left(struct list_head *head) > +{ > + struct list_head *first; > + > + if (!list_empty(head)) { > + first = head->next; > + list_move_tail(first, head); > + } > +} > + > +/** > + * * list_is_singular - tests whether a list has just one entry. > + * * @head: the list to test. > + * */ > +static inline int list_is_singular(const struct list_head *head) > +{ > + return !list_empty(head) && (head->next == head->prev); > +} > + > +static inline void __list_cut_position(struct list_head *list, > + struct list_head *head, struct list_head *entry) > +{ > + struct list_head *new_first = entry->next; > + list->next = head->next; > + list->next->prev = list; > + list->prev = entry; > + entry->next = list; > + head->next = new_first; > + new_first->prev = head; > +} > + > +/** > + * * list_cut_position - cut a list into two > + * * @list: a new list to add all removed entries > + * * @head: a list with entries > + * * @entry: an entry within head, could be the head itself > + * * and if so we won't cut the list > + * * > + * * This helper moves the initial part of @head, up to and > + * * including @entry, from @head to @list. You should > + * * pass on @entry an element you know is on @head. @list > + * * should be an empty list or a list you do not care about > + * * losing its data. > + * * > + * */ > +static inline void list_cut_position(struct list_head *list, > + struct list_head *head, struct list_head *entry) > +{ > + if (list_empty(head)) > + return; > + if (list_is_singular(head) && > + (head->next != entry && head != entry)) > + return; > + if (entry == head) > + INIT_LIST_HEAD(list); > + else > + __list_cut_position(list, head, entry); > +} > + > +static inline void __list_splice(const struct list_head *list, > + struct list_head *prev, > + struct list_head *next) > +{ > + struct list_head *first = list->next; > + struct list_head *last = list->prev; > + > + first->prev = prev; > + prev->next = first; > + > + last->next = next; > + next->prev = last; > +} > + > +/** > + * * list_splice - join two lists, this is designed for stacks > + * * @list: the new list to add. > + * * @head: the place to add it in the first list. > + * */ > +static inline void list_splice(const struct list_head *list, > + struct list_head *head) > +{ > + if (!list_empty(list)) > + __list_splice(list, head, head->next); > +} > + > +/** > + * * list_splice_tail - join two lists, each list being a queue > + * * @list: the new list to add. > + * * @head: the place to add it in the first list. > + * */ > +static inline void list_splice_tail(struct list_head *list, > + struct list_head *head) > +{ > + if (!list_empty(list)) > + __list_splice(list, head->prev, head); > +} > + > +/** > + * * list_splice_init - join two lists and reinitialise the emptied list. > + * * @list: the new list to add. > + * * @head: the place to add it in the first list. > + * * > + * * The list at @list is reinitialised > + * */ > +static inline void list_splice_init(struct list_head *list, > + struct list_head *head) > +{ > + if (!list_empty(list)) { > + __list_splice(list, head, head->next); > + INIT_LIST_HEAD(list); > + } > +} > + > +/** > + * * list_splice_tail_init - join two lists and reinitialise the emptied list > + * * @list: the new list to add. > + * * @head: the place to add it in the first list. > + * * > + * * Each of the lists is a queue. > + * * The list at @list is reinitialised > + * */ > +static inline void list_splice_tail_init(struct list_head *list, > + struct list_head *head) > +{ > + if (!list_empty(list)) { > + __list_splice(list, head->prev, head); > + INIT_LIST_HEAD(list); > + } > +} > + > +/** > + * * list_entry - get the struct for this entry > + * * @ptr: the &struct list_head pointer. > + * * @type: the type of the struct this is embedded in. > + * * @member: the name of the list_struct within the struct. > + * */ > +#define list_entry(ptr, type, member) \ > + container_of(ptr, type, member) > + > +/** > + * * list_first_entry - get the first element from a list > + * * @ptr: the list head to take the element from. > + * * @type: the type of the struct this is embedded in. > + * * @member: the name of the list_struct within the struct. > + * * > + * * Note, that list is expected to be not empty. > + * */ > +#define list_first_entry(ptr, type, member) \ > + list_entry((ptr)->next, type, member) > + > +/** > + * * list_for_each - iterate over a list > + * * @pos: the &struct list_head to use as a loop cursor. > + * * @head: the head for your list. > + * */ > +#define list_for_each(pos, head) \ > + for (pos = (head)->next; pos != (head); pos = pos->next) > + > +/** > + * * __list_for_each - iterate over a list > + * * @pos: the &struct list_head to use as a loop cursor. > + * * @head: the head for your list. > + * * > + * * This variant doesn't differ from list_for_each() any more. > + * * We don't do prefetching in either case. > + * */ > +#define __list_for_each(pos, head) \ > + for (pos = (head)->next; pos != (head); pos = pos->next) > + > +/** > + * * list_for_each_prev - iterate over a list backwards > + * * @pos: the &struct list_head to use as a loop cursor. > + * * @head: the head for your list. > + * */ > +#define list_for_each_prev(pos, head) \ > + for (pos = (head)->prev; pos != (head); pos = pos->prev) > + > +/** > + * * list_for_each_safe - iterate over a list safe against removal of list entry > + * * @pos: the &struct list_head to use as a loop cursor. > + * * @n: another &struct list_head to use as temporary storage > + * * @head: the head for your list. > + * */ > +#define list_for_each_safe(pos, n, head) \ > + for (pos = (head)->next, n = pos->next; pos != (head); \ > + pos = n, n = pos->next) > + > +/** > + * * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry > + * * @pos: the &struct list_head to use as a loop cursor. > + * * @n: another &struct list_head to use as temporary storage > + * * @head: the head for your list. > + * */ > +#define list_for_each_prev_safe(pos, n, head) \ > + for (pos = (head)->prev, n = pos->prev; \ > + pos != (head); \ > + pos = n, n = pos->prev) > + > +/** > + * * list_for_each_entry - iterate over list of given type > + * * @pos: the type * to use as a loop cursor. > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * */ > +#define list_for_each_entry(pos, head, member) \ > + for (pos = list_entry((head)->next, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = list_entry(pos->member.next, typeof(*pos), member)) > + > +/** > + * * list_for_each_entry_reverse - iterate backwards over list of given type. > + * * @pos: the type * to use as a loop cursor. > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * */ > +#define list_for_each_entry_reverse(pos, head, member) \ > + for (pos = list_entry((head)->prev, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = list_entry(pos->member.prev, typeof(*pos), member)) > + > +/** > + * * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() > + * * @pos: the type * to use as a start point > + * * @head: the head of the list > + * * @member: the name of the list_struct within the struct. > + * * > + * * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). > + * */ > +#define list_prepare_entry(pos, head, member) \ > + ((pos) ? : list_entry(head, typeof(*pos), member)) > + > +/** > + * * list_for_each_entry_continue - continue iteration over list of given type > + * * @pos: the type * to use as a loop cursor. > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * * > + * * Continue to iterate over list of given type, continuing after > + * * the current position. > + * */ > +#define list_for_each_entry_continue(pos, head, member) \ > + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = list_entry(pos->member.next, typeof(*pos), member)) > + > +/** > + * * list_for_each_entry_continue_reverse - iterate backwards from the given point > + * * @pos: the type * to use as a loop cursor. > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * * > + * * Start to iterate over list of given type backwards, continuing after > + * * the current position. > + * */ > +#define list_for_each_entry_continue_reverse(pos, head, member) \ > + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = list_entry(pos->member.prev, typeof(*pos), member)) > + > +/** > + * * list_for_each_entry_from - iterate over list of given type from the current point > + * * @pos: the type * to use as a loop cursor. > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * * > + * * Iterate over list of given type, continuing from current position. > + * */ > +#define list_for_each_entry_from(pos, head, member) \ > + for (; &pos->member != (head); \ > + pos = list_entry(pos->member.next, typeof(*pos), member)) > + > +/** > + * * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry > + * * @pos: the type * to use as a loop cursor. > + * * @n: another type * to use as temporary storage > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * */ > +#define list_for_each_entry_safe(pos, n, head, member) \ > + for (pos = list_entry((head)->next, typeof(*pos), member), \ > + n = list_entry(pos->member.next, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = n, n = list_entry(n->member.next, typeof(*n), member)) > + > +/** > + * * list_for_each_entry_safe_continue - continue list iteration safe against removal > + * * @pos: the type * to use as a loop cursor. > + * * @n: another type * to use as temporary storage > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * * > + * * Iterate over list of given type, continuing after current point, > + * * safe against removal of list entry. > + * */ > +#define list_for_each_entry_safe_continue(pos, n, head, member) \ > + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ > + n = list_entry(pos->member.next, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = n, n = list_entry(n->member.next, typeof(*n), member)) > + > +/** > + * * list_for_each_entry_safe_from - iterate over list from current point safe against removal > + * * @pos: the type * to use as a loop cursor. > + * * @n: another type * to use as temporary storage > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * * > + * * Iterate over list of given type from current point, safe against > + * * removal of list entry. > + * */ > +#define list_for_each_entry_safe_from(pos, n, head, member) \ > + for (n = list_entry(pos->member.next, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = n, n = list_entry(n->member.next, typeof(*n), member)) > + > +/** > + * * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal > + * * @pos: the type * to use as a loop cursor. > + * * @n: another type * to use as temporary storage > + * * @head: the head for your list. > + * * @member: the name of the list_struct within the struct. > + * * > + * * Iterate backwards over list of given type, safe against removal > + * * of list entry. > + * */ > +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ > + for (pos = list_entry((head)->prev, typeof(*pos), member), \ > + n = list_entry(pos->member.prev, typeof(*pos), member); \ > + &pos->member != (head); \ > + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) > + > +/** > + * * list_safe_reset_next - reset a stale list_for_each_entry_safe loop > + * * @pos: the loop cursor used in the list_for_each_entry_safe loop > + * * @n: temporary storage used in list_for_each_entry_safe > + * * @member: the name of the list_struct within the struct. > + * * > + * * list_safe_reset_next is not safe to use in general if the list may be > + * * modified concurrently (eg. the lock is dropped in the loop body). An > + * * exception to this is if the cursor element (pos) is pinned in the list, > + * * and list_safe_reset_next is called after re-taking the lock and before > + * * completing the current iteration of the loop body. > + * */ > +#define list_safe_reset_next(pos, n, member) \ > + n = list_entry(pos->member.next, typeof(*pos), member) > + > +/* > + * * Double linked lists with a single pointer list head. > + * * Mostly useful for hash tables where the two pointer list head is > + * * too wasteful. > + * * You lose the ability to access the tail in O(1). > + * */ > + > +#define HLIST_HEAD_INIT { .first = NULL } > +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } > +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) > +static inline void INIT_HLIST_NODE(struct hlist_node *h) > +{ > + h->next = NULL; > + h->pprev = NULL; > +} > + > +static inline int hlist_unhashed(const struct hlist_node *h) > +{ > + return !h->pprev; > +} > + > +static inline int hlist_empty(const struct hlist_head *h) > +{ > + return !h->first; > +} > + > +static inline void __hlist_del(struct hlist_node *n) > +{ > + struct hlist_node *next = n->next; > + struct hlist_node **pprev = n->pprev; > + *pprev = next; > + if (next) > + next->pprev = pprev; > +} > + > +static inline void hlist_del(struct hlist_node *n) > +{ > + __hlist_del(n); > + n->next = LIST_POISON1; > + n->pprev = LIST_POISON2; > +} > + > +static inline void hlist_del_init(struct hlist_node *n) > +{ > + if (!hlist_unhashed(n)) { > + __hlist_del(n); > + INIT_HLIST_NODE(n); > + } > +} > + > +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) > +{ > + struct hlist_node *first = h->first; > + n->next = first; > + if (first) > + first->pprev = &n->next; > + h->first = n; > + n->pprev = &h->first; > +} > + > +/* next must be != NULL */ > +static inline void hlist_add_before(struct hlist_node *n, > + struct hlist_node *next) > +{ > + n->pprev = next->pprev; > + n->next = next; > + next->pprev = &n->next; > + *(n->pprev) = n; > +} > + > +static inline void hlist_add_after(struct hlist_node *n, > + struct hlist_node *next) > +{ > + next->next = n->next; > + n->next = next; > + next->pprev = &n->next; > + > + if(next->next) > + next->next->pprev = &next->next; > +} > + > +/* after that we'll appear to be on some hlist and hlist_del will work */ > +static inline void hlist_add_fake(struct hlist_node *n) > +{ > + n->pprev = &n->next; > +} > + > +/* > + * * Move a list from one list head to another. Fixup the pprev > + * * reference of the first entry if it exists. > + * */ > +static inline void hlist_move_list(struct hlist_head *old, > + struct hlist_head *new) > +{ > + new->first = old->first; > + if (new->first) > + new->first->pprev = &new->first; > + old->first = NULL; > +} > + > +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) > + > +#define hlist_for_each(pos, head) \ > + for (pos = (head)->first; pos ; pos = pos->next) > + > +#define hlist_for_each_safe(pos, n, head) \ > + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ > + pos = n) > + > +/** > + * * hlist_for_each_entry - iterate over list of given type > + * * @tpos: the type * to use as a loop cursor. > + * * @pos: the &struct hlist_node to use as a loop cursor. > + * * @head: the head for your list. > + * * @member: the name of the hlist_node within the struct. > + * */ > +#define hlist_for_each_entry(tpos, pos, head, member) \ > + for (pos = (head)->first; \ > + pos && \ > + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ > + pos = pos->next) > + > +/** > + * * hlist_for_each_entry_continue - iterate over a hlist continuing after current point > + * * @tpos: the type * to use as a loop cursor. > + * * @pos: the &struct hlist_node to use as a loop cursor. > + * * @member: the name of the hlist_node within the struct. > + * */ > +#define hlist_for_each_entry_continue(tpos, pos, member) \ > + for (pos = (pos)->next; \ > + pos && \ > + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ > + pos = pos->next) > + > +/** > + * * hlist_for_each_entry_from - iterate over a hlist continuing from current point > + * * @tpos: the type * to use as a loop cursor. > + * * @pos: the &struct hlist_node to use as a loop cursor. > + * * @member: the name of the hlist_node within the struct. > + * */ > +#define hlist_for_each_entry_from(tpos, pos, member) \ > + for (; pos && \ > + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ > + pos = pos->next) > + > +/** > + * * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry > + * * @tpos: the type * to use as a loop cursor. > + * * @pos: the &struct hlist_node to use as a loop cursor. > + * * @n: another &struct hlist_node to use as temporary storage > + * * @head: the head for your list. > + * * @member: the name of the hlist_node within the struct. > + * */ > +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ > + for (pos = (head)->first; \ > + pos && ({ n = pos->next; 1; }) && \ > + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ > + pos = n) > + > +#endif > diff --git a/make-bcache.c b/make-bcache.c > index c626eae..3140a3a 100644 > --- a/make-bcache.c > +++ b/make-bcache.c > @@ -335,7 +335,7 @@ static unsigned get_blocksize(const char *path) > return statbuf.st_blksize / 512; > } > > -int main(int argc, char **argv) > +int make_bcache(int argc, char **argv) > { > int c, bdev = -1; > unsigned i, ncache_devices = 0, nbacking_devices = 0; > diff --git a/make-bcache.h b/make-bcache.h > new file mode 100644 > index 0000000..3681737 > --- /dev/null > +++ b/make-bcache.h > @@ -0,0 +1 @@ > +extern int make_bcache(int argc, char **argv); > diff --git a/package/bcache-ctl.spec b/package/bcache-ctl.spec > new file mode 100755 > index 0000000..ae58b7b > --- /dev/null > +++ b/package/bcache-ctl.spec > @@ -0,0 +1,55 @@ > +%global debug_package %{nil} > +%global __strip /bin/true > + > +Name: bcache-ctl > +Version: %{ver} > +Release: %{rel}%{?dist} > + > +Summary: This is an command line tool for bcache,which used to manage bcache devices. > + > +Group: Storage > +License: GPL > +URL: https://github.com/dahefanteng/bcache-ctl > +Source0: %{name}-%{version}-%{rel}.tar.gz > +BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) > + > +%description > + > + > +%prep > +%setup -q -n %{name}-%{version}-%{rel} > + > + > +%build > +make > + > +%install > +rm -rf %{buildroot} > +install -D -m 755 %{_builddir}/bcache-ctl-%{version}-%{rel}/bcache-ctl %{buildroot}%{_bindir}/bcache-ctl > +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/probe-bcache %{buildroot}/lib/udev/probe-bcache > +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/bcache-register %{buildroot}/lib/udev/bcache-register > +install -D -m0644 %{_builddir}/bcache-ctl-%{version}-%{rel}/69-bcache.rules %{buildroot}/lib/udev/rules.d/69-bcache.rules > +install -D -m0644 %{_builddir}/bcache-ctl-%{version}-%{rel}/probe-bcache.8 %{buildroot}/usr/share/man/man8/probe-bcache.8 > +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/initramfs/hook %{buildroot}/usr/share/initramfs-tools/hooks/bcache > +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/initcpio/install %{buildroot}/usr/lib/initcpio/install/bcache > +#conflict with dracut? > +#install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/dracut/module-setup.sh %{buildroot}/lib/dracut/modules.d/90bcache/module-setup.sh > + > +%post > + > +%preun > + > +%clean > +rm -rf %{buildroot} > + > +%files > +%defattr(-,root,root,-) > +/usr/bin/bcache-ctl > +/lib/udev/probe-bcache > +/lib/udev/bcache-register > +/lib/udev/rules.d/69-bcache.rules > +/usr/share/man/man8/probe-bcache.8.gz > +/usr/share/initramfs-tools/hooks/bcache > +/usr/lib/initcpio/install/bcache > +#/lib/dracut/modules.d/90bcache/module-setup.sh > +%changelog > diff --git a/package/rpmbuild.sh b/package/rpmbuild.sh > new file mode 100755 > index 0000000..6eb8612 > --- /dev/null > +++ b/package/rpmbuild.sh > @@ -0,0 +1,29 @@ > +#!/bin/bash > + > +PACKAGENAME=bcache-ctl > +echo Building RPMs.. > +GITROOT=`git rev-parse --show-toplevel` > +cd $GITROOT > +VER=1.0 > +REL=`git rev-parse --short HEAD`git > +REL=`git log --oneline|wc -l`.$REL > +RPMTOPDIR=$GITROOT/rpm-build > +echo "Ver: $VER, Release: $REL" > + > + > +rm -rf $RPMTOPDIR > +# Create tarball > +mkdir -p $RPMTOPDIR/{SOURCES,SPECS} > +git archive --format=tar --prefix=${PACKAGENAME}-${VER}-${REL}/ HEAD | gzip -c > $RPMTOPDIR/SOURCES/${PACKAGENAME}-${VER}-${REL}.tar.gz > + > +# Convert git log to RPM's ChangeLog format (shown with rpm -qp --changelog <rpm file>) > +sed -e "s/%{ver}/$VER/" -e "s/%{rel}/$REL/" $GITROOT/package/${PACKAGENAME}.spec > $RPMTOPDIR/SPECS/${PACKAGENAME}.spec > +git log -n 10 --format="* %cd %aN%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $RPMTOPDIR/SPECS/${PACKAGENAME}.spec > +# Build SRC and binary RPMs > +rpmbuild \ > +--define "_topdir $RPMTOPDIR" \ > +--define "_rpmdir $PWD" \ > +--define "_srcrpmdir $PWD" \ > +--define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ > +-ba $RPMTOPDIR/SPECS/${PACKAGENAME}.spec > + > -- > 1.8.3.1 >