Re: [PATCH 1/1] bcache-tools: Replace make-bcache,bcache-super-show using bcache-ctl

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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(&reg, pattern, REG_EXTENDED) != 0) {
> +               fprintf(stderr, "Error happen when check uuid format:%m");
> +       }
> +       status = regexec(&reg, uuid, 1, &regmatche, 0);
> +       regfree(&reg);
> +       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(&reg, pattern, REG_EXTENDED) != 0) {
> +               fprintf(stderr,
> +                       "Error happen when check device name format:%m");
> +       }
> +       status = regexec(&reg, devname, 1, &regmatche, 0);
> +       regfree(&reg);
> +       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
>




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux ARM Kernel]     [Linux Filesystem Development]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux