linux/net/ceph/ceph_common.c
<<
>>
Prefs
   1
   2#include <linux/ceph/ceph_debug.h>
   3#include <linux/backing-dev.h>
   4#include <linux/ctype.h>
   5#include <linux/fs.h>
   6#include <linux/inet.h>
   7#include <linux/in6.h>
   8#include <linux/key.h>
   9#include <keys/ceph-type.h>
  10#include <linux/module.h>
  11#include <linux/mount.h>
  12#include <linux/parser.h>
  13#include <linux/sched.h>
  14#include <linux/seq_file.h>
  15#include <linux/slab.h>
  16#include <linux/statfs.h>
  17#include <linux/string.h>
  18
  19
  20#include <linux/ceph/libceph.h>
  21#include <linux/ceph/debugfs.h>
  22#include <linux/ceph/decode.h>
  23#include <linux/ceph/mon_client.h>
  24#include <linux/ceph/auth.h>
  25#include "crypto.h"
  26
  27
  28
  29/*
  30 * find filename portion of a path (/foo/bar/baz -> baz)
  31 */
  32const char *ceph_file_part(const char *s, int len)
  33{
  34        const char *e = s + len;
  35
  36        while (e != s && *(e-1) != '/')
  37                e--;
  38        return e;
  39}
  40EXPORT_SYMBOL(ceph_file_part);
  41
  42const char *ceph_msg_type_name(int type)
  43{
  44        switch (type) {
  45        case CEPH_MSG_SHUTDOWN: return "shutdown";
  46        case CEPH_MSG_PING: return "ping";
  47        case CEPH_MSG_AUTH: return "auth";
  48        case CEPH_MSG_AUTH_REPLY: return "auth_reply";
  49        case CEPH_MSG_MON_MAP: return "mon_map";
  50        case CEPH_MSG_MON_GET_MAP: return "mon_get_map";
  51        case CEPH_MSG_MON_SUBSCRIBE: return "mon_subscribe";
  52        case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack";
  53        case CEPH_MSG_STATFS: return "statfs";
  54        case CEPH_MSG_STATFS_REPLY: return "statfs_reply";
  55        case CEPH_MSG_MDS_MAP: return "mds_map";
  56        case CEPH_MSG_CLIENT_SESSION: return "client_session";
  57        case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect";
  58        case CEPH_MSG_CLIENT_REQUEST: return "client_request";
  59        case CEPH_MSG_CLIENT_REQUEST_FORWARD: return "client_request_forward";
  60        case CEPH_MSG_CLIENT_REPLY: return "client_reply";
  61        case CEPH_MSG_CLIENT_CAPS: return "client_caps";
  62        case CEPH_MSG_CLIENT_CAPRELEASE: return "client_cap_release";
  63        case CEPH_MSG_CLIENT_SNAP: return "client_snap";
  64        case CEPH_MSG_CLIENT_LEASE: return "client_lease";
  65        case CEPH_MSG_OSD_MAP: return "osd_map";
  66        case CEPH_MSG_OSD_OP: return "osd_op";
  67        case CEPH_MSG_OSD_OPREPLY: return "osd_opreply";
  68        case CEPH_MSG_WATCH_NOTIFY: return "watch_notify";
  69        default: return "unknown";
  70        }
  71}
  72EXPORT_SYMBOL(ceph_msg_type_name);
  73
  74/*
  75 * Initially learn our fsid, or verify an fsid matches.
  76 */
  77int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid)
  78{
  79        if (client->have_fsid) {
  80                if (ceph_fsid_compare(&client->fsid, fsid)) {
  81                        pr_err("bad fsid, had %pU got %pU",
  82                               &client->fsid, fsid);
  83                        return -1;
  84                }
  85        } else {
  86                pr_info("client%lld fsid %pU\n", ceph_client_id(client), fsid);
  87                memcpy(&client->fsid, fsid, sizeof(*fsid));
  88        }
  89        return 0;
  90}
  91EXPORT_SYMBOL(ceph_check_fsid);
  92
  93static int strcmp_null(const char *s1, const char *s2)
  94{
  95        if (!s1 && !s2)
  96                return 0;
  97        if (s1 && !s2)
  98                return -1;
  99        if (!s1 && s2)
 100                return 1;
 101        return strcmp(s1, s2);
 102}
 103
 104int ceph_compare_options(struct ceph_options *new_opt,
 105                         struct ceph_client *client)
 106{
 107        struct ceph_options *opt1 = new_opt;
 108        struct ceph_options *opt2 = client->options;
 109        int ofs = offsetof(struct ceph_options, mon_addr);
 110        int i;
 111        int ret;
 112
 113        ret = memcmp(opt1, opt2, ofs);
 114        if (ret)
 115                return ret;
 116
 117        ret = strcmp_null(opt1->name, opt2->name);
 118        if (ret)
 119                return ret;
 120
 121        if (opt1->key && !opt2->key)
 122                return -1;
 123        if (!opt1->key && opt2->key)
 124                return 1;
 125        if (opt1->key && opt2->key) {
 126                if (opt1->key->type != opt2->key->type)
 127                        return -1;
 128                if (opt1->key->created.tv_sec != opt2->key->created.tv_sec)
 129                        return -1;
 130                if (opt1->key->created.tv_nsec != opt2->key->created.tv_nsec)
 131                        return -1;
 132                if (opt1->key->len != opt2->key->len)
 133                        return -1;
 134                if (opt1->key->key && !opt2->key->key)
 135                        return -1;
 136                if (!opt1->key->key && opt2->key->key)
 137                        return 1;
 138                if (opt1->key->key && opt2->key->key) {
 139                        ret = memcmp(opt1->key->key, opt2->key->key, opt1->key->len);
 140                        if (ret)
 141                                return ret;
 142                }
 143        }
 144
 145        /* any matching mon ip implies a match */
 146        for (i = 0; i < opt1->num_mon; i++) {
 147                if (ceph_monmap_contains(client->monc.monmap,
 148                                 &opt1->mon_addr[i]))
 149                        return 0;
 150        }
 151        return -1;
 152}
 153EXPORT_SYMBOL(ceph_compare_options);
 154
 155
 156static int parse_fsid(const char *str, struct ceph_fsid *fsid)
 157{
 158        int i = 0;
 159        char tmp[3];
 160        int err = -EINVAL;
 161        int d;
 162
 163        dout("parse_fsid '%s'\n", str);
 164        tmp[2] = 0;
 165        while (*str && i < 16) {
 166                if (ispunct(*str)) {
 167                        str++;
 168                        continue;
 169                }
 170                if (!isxdigit(str[0]) || !isxdigit(str[1]))
 171                        break;
 172                tmp[0] = str[0];
 173                tmp[1] = str[1];
 174                if (sscanf(tmp, "%x", &d) < 1)
 175                        break;
 176                fsid->fsid[i] = d & 0xff;
 177                i++;
 178                str += 2;
 179        }
 180
 181        if (i == 16)
 182                err = 0;
 183        dout("parse_fsid ret %d got fsid %pU", err, fsid);
 184        return err;
 185}
 186
 187/*
 188 * ceph options
 189 */
 190enum {
 191        Opt_osdtimeout,
 192        Opt_osdkeepalivetimeout,
 193        Opt_mount_timeout,
 194        Opt_osd_idle_ttl,
 195        Opt_last_int,
 196        /* int args above */
 197        Opt_fsid,
 198        Opt_name,
 199        Opt_secret,
 200        Opt_key,
 201        Opt_ip,
 202        Opt_last_string,
 203        /* string args above */
 204        Opt_share,
 205        Opt_noshare,
 206        Opt_crc,
 207        Opt_nocrc,
 208};
 209
 210static match_table_t opt_tokens = {
 211        {Opt_osdtimeout, "osdtimeout=%d"},
 212        {Opt_osdkeepalivetimeout, "osdkeepalive=%d"},
 213        {Opt_mount_timeout, "mount_timeout=%d"},
 214        {Opt_osd_idle_ttl, "osd_idle_ttl=%d"},
 215        /* int args above */
 216        {Opt_fsid, "fsid=%s"},
 217        {Opt_name, "name=%s"},
 218        {Opt_secret, "secret=%s"},
 219        {Opt_key, "key=%s"},
 220        {Opt_ip, "ip=%s"},
 221        /* string args above */
 222        {Opt_share, "share"},
 223        {Opt_noshare, "noshare"},
 224        {Opt_crc, "crc"},
 225        {Opt_nocrc, "nocrc"},
 226        {-1, NULL}
 227};
 228
 229void ceph_destroy_options(struct ceph_options *opt)
 230{
 231        dout("destroy_options %p\n", opt);
 232        kfree(opt->name);
 233        if (opt->key) {
 234                ceph_crypto_key_destroy(opt->key);
 235                kfree(opt->key);
 236        }
 237        kfree(opt->mon_addr);
 238        kfree(opt);
 239}
 240EXPORT_SYMBOL(ceph_destroy_options);
 241
 242/* get secret from key store */
 243static int get_secret(struct ceph_crypto_key *dst, const char *name) {
 244        struct key *ukey;
 245        int key_err;
 246        int err = 0;
 247        struct ceph_crypto_key *ckey;
 248
 249        ukey = request_key(&key_type_ceph, name, NULL);
 250        if (!ukey || IS_ERR(ukey)) {
 251                /* request_key errors don't map nicely to mount(2)
 252                   errors; don't even try, but still printk */
 253                key_err = PTR_ERR(ukey);
 254                switch (key_err) {
 255                case -ENOKEY:
 256                        pr_warning("ceph: Mount failed due to key not found: %s\n", name);
 257                        break;
 258                case -EKEYEXPIRED:
 259                        pr_warning("ceph: Mount failed due to expired key: %s\n", name);
 260                        break;
 261                case -EKEYREVOKED:
 262                        pr_warning("ceph: Mount failed due to revoked key: %s\n", name);
 263                        break;
 264                default:
 265                        pr_warning("ceph: Mount failed due to unknown key error"
 266                               " %d: %s\n", key_err, name);
 267                }
 268                err = -EPERM;
 269                goto out;
 270        }
 271
 272        ckey = ukey->payload.data;
 273        err = ceph_crypto_key_clone(dst, ckey);
 274        if (err)
 275                goto out_key;
 276        /* pass through, err is 0 */
 277
 278out_key:
 279        key_put(ukey);
 280out:
 281        return err;
 282}
 283
 284struct ceph_options *
 285ceph_parse_options(char *options, const char *dev_name,
 286                        const char *dev_name_end,
 287                        int (*parse_extra_token)(char *c, void *private),
 288                        void *private)
 289{
 290        struct ceph_options *opt;
 291        const char *c;
 292        int err = -ENOMEM;
 293        substring_t argstr[MAX_OPT_ARGS];
 294
 295        opt = kzalloc(sizeof(*opt), GFP_KERNEL);
 296        if (!opt)
 297                return ERR_PTR(-ENOMEM);
 298        opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr),
 299                                GFP_KERNEL);
 300        if (!opt->mon_addr)
 301                goto out;
 302
 303        dout("parse_options %p options '%s' dev_name '%s'\n", opt, options,
 304             dev_name);
 305
 306        /* start with defaults */
 307        opt->flags = CEPH_OPT_DEFAULT;
 308        opt->osd_timeout = CEPH_OSD_TIMEOUT_DEFAULT;
 309        opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
 310        opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; /* seconds */
 311        opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;   /* seconds */
 312
 313        /* get mon ip(s) */
 314        /* ip1[:port1][,ip2[:port2]...] */
 315        err = ceph_parse_ips(dev_name, dev_name_end, opt->mon_addr,
 316                             CEPH_MAX_MON, &opt->num_mon);
 317        if (err < 0)
 318                goto out;
 319
 320        /* parse mount options */
 321        while ((c = strsep(&options, ",")) != NULL) {
 322                int token, intval, ret;
 323                if (!*c)
 324                        continue;
 325                err = -EINVAL;
 326                token = match_token((char *)c, opt_tokens, argstr);
 327                if (token < 0 && parse_extra_token) {
 328                        /* extra? */
 329                        err = parse_extra_token((char *)c, private);
 330                        if (err < 0) {
 331                                pr_err("bad option at '%s'\n", c);
 332                                goto out;
 333                        }
 334                        continue;
 335                }
 336                if (token < Opt_last_int) {
 337                        ret = match_int(&argstr[0], &intval);
 338                        if (ret < 0) {
 339                                pr_err("bad mount option arg (not int) "
 340                                       "at '%s'\n", c);
 341                                continue;
 342                        }
 343                        dout("got int token %d val %d\n", token, intval);
 344                } else if (token > Opt_last_int && token < Opt_last_string) {
 345                        dout("got string token %d val %s\n", token,
 346                             argstr[0].from);
 347                } else {
 348                        dout("got token %d\n", token);
 349                }
 350                switch (token) {
 351                case Opt_ip:
 352                        err = ceph_parse_ips(argstr[0].from,
 353                                             argstr[0].to,
 354                                             &opt->my_addr,
 355                                             1, NULL);
 356                        if (err < 0)
 357                                goto out;
 358                        opt->flags |= CEPH_OPT_MYIP;
 359                        break;
 360
 361                case Opt_fsid:
 362                        err = parse_fsid(argstr[0].from, &opt->fsid);
 363                        if (err == 0)
 364                                opt->flags |= CEPH_OPT_FSID;
 365                        break;
 366                case Opt_name:
 367                        opt->name = kstrndup(argstr[0].from,
 368                                              argstr[0].to-argstr[0].from,
 369                                              GFP_KERNEL);
 370                        break;
 371                case Opt_secret:
 372                        opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
 373                        if (!opt->key) {
 374                                err = -ENOMEM;
 375                                goto out;
 376                        }
 377                        err = ceph_crypto_key_unarmor(opt->key, argstr[0].from);
 378                        if (err < 0)
 379                                goto out;
 380                        break;
 381                case Opt_key:
 382                        opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
 383                        if (!opt->key) {
 384                                err = -ENOMEM;
 385                                goto out;
 386                        }
 387                        err = get_secret(opt->key, argstr[0].from);
 388                        if (err < 0)
 389                                goto out;
 390                        break;
 391
 392                        /* misc */
 393                case Opt_osdtimeout:
 394                        opt->osd_timeout = intval;
 395                        break;
 396                case Opt_osdkeepalivetimeout:
 397                        opt->osd_keepalive_timeout = intval;
 398                        break;
 399                case Opt_osd_idle_ttl:
 400                        opt->osd_idle_ttl = intval;
 401                        break;
 402                case Opt_mount_timeout:
 403                        opt->mount_timeout = intval;
 404                        break;
 405
 406                case Opt_share:
 407                        opt->flags &= ~CEPH_OPT_NOSHARE;
 408                        break;
 409                case Opt_noshare:
 410                        opt->flags |= CEPH_OPT_NOSHARE;
 411                        break;
 412
 413                case Opt_crc:
 414                        opt->flags &= ~CEPH_OPT_NOCRC;
 415                        break;
 416                case Opt_nocrc:
 417                        opt->flags |= CEPH_OPT_NOCRC;
 418                        break;
 419
 420                default:
 421                        BUG_ON(token);
 422                }
 423        }
 424
 425        /* success */
 426        return opt;
 427
 428out:
 429        ceph_destroy_options(opt);
 430        return ERR_PTR(err);
 431}
 432EXPORT_SYMBOL(ceph_parse_options);
 433
 434u64 ceph_client_id(struct ceph_client *client)
 435{
 436        return client->monc.auth->global_id;
 437}
 438EXPORT_SYMBOL(ceph_client_id);
 439
 440/*
 441 * create a fresh client instance
 442 */
 443struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private,
 444                                       unsigned int supported_features,
 445                                       unsigned int required_features)
 446{
 447        struct ceph_client *client;
 448        struct ceph_entity_addr *myaddr = NULL;
 449        int err = -ENOMEM;
 450
 451        client = kzalloc(sizeof(*client), GFP_KERNEL);
 452        if (client == NULL)
 453                return ERR_PTR(-ENOMEM);
 454
 455        client->private = private;
 456        client->options = opt;
 457
 458        mutex_init(&client->mount_mutex);
 459        init_waitqueue_head(&client->auth_wq);
 460        client->auth_err = 0;
 461
 462        client->extra_mon_dispatch = NULL;
 463        client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT |
 464                supported_features;
 465        client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT |
 466                required_features;
 467
 468        /* msgr */
 469        if (ceph_test_opt(client, MYIP))
 470                myaddr = &client->options->my_addr;
 471        client->msgr = ceph_messenger_create(myaddr,
 472                                             client->supported_features,
 473                                             client->required_features);
 474        if (IS_ERR(client->msgr)) {
 475                err = PTR_ERR(client->msgr);
 476                goto fail;
 477        }
 478        client->msgr->nocrc = ceph_test_opt(client, NOCRC);
 479
 480        /* subsystems */
 481        err = ceph_monc_init(&client->monc, client);
 482        if (err < 0)
 483                goto fail_msgr;
 484        err = ceph_osdc_init(&client->osdc, client);
 485        if (err < 0)
 486                goto fail_monc;
 487
 488        return client;
 489
 490fail_monc:
 491        ceph_monc_stop(&client->monc);
 492fail_msgr:
 493        ceph_messenger_destroy(client->msgr);
 494fail:
 495        kfree(client);
 496        return ERR_PTR(err);
 497}
 498EXPORT_SYMBOL(ceph_create_client);
 499
 500void ceph_destroy_client(struct ceph_client *client)
 501{
 502        dout("destroy_client %p\n", client);
 503
 504        /* unmount */
 505        ceph_osdc_stop(&client->osdc);
 506
 507        ceph_monc_stop(&client->monc);
 508
 509        ceph_debugfs_client_cleanup(client);
 510
 511        ceph_messenger_destroy(client->msgr);
 512
 513        ceph_destroy_options(client->options);
 514
 515        kfree(client);
 516        dout("destroy_client %p done\n", client);
 517}
 518EXPORT_SYMBOL(ceph_destroy_client);
 519
 520/*
 521 * true if we have the mon map (and have thus joined the cluster)
 522 */
 523static int have_mon_and_osd_map(struct ceph_client *client)
 524{
 525        return client->monc.monmap && client->monc.monmap->epoch &&
 526               client->osdc.osdmap && client->osdc.osdmap->epoch;
 527}
 528
 529/*
 530 * mount: join the ceph cluster, and open root directory.
 531 */
 532int __ceph_open_session(struct ceph_client *client, unsigned long started)
 533{
 534        int err;
 535        unsigned long timeout = client->options->mount_timeout * HZ;
 536
 537        /* open session, and wait for mon and osd maps */
 538        err = ceph_monc_open_session(&client->monc);
 539        if (err < 0)
 540                return err;
 541
 542        while (!have_mon_and_osd_map(client)) {
 543                err = -EIO;
 544                if (timeout && time_after_eq(jiffies, started + timeout))
 545                        return err;
 546
 547                /* wait */
 548                dout("mount waiting for mon_map\n");
 549                err = wait_event_interruptible_timeout(client->auth_wq,
 550                        have_mon_and_osd_map(client) || (client->auth_err < 0),
 551                        timeout);
 552                if (err == -EINTR || err == -ERESTARTSYS)
 553                        return err;
 554                if (client->auth_err < 0)
 555                        return client->auth_err;
 556        }
 557
 558        return 0;
 559}
 560EXPORT_SYMBOL(__ceph_open_session);
 561
 562
 563int ceph_open_session(struct ceph_client *client)
 564{
 565        int ret;
 566        unsigned long started = jiffies;  /* note the start time */
 567
 568        dout("open_session start\n");
 569        mutex_lock(&client->mount_mutex);
 570
 571        ret = __ceph_open_session(client, started);
 572
 573        mutex_unlock(&client->mount_mutex);
 574        return ret;
 575}
 576EXPORT_SYMBOL(ceph_open_session);
 577
 578
 579static int __init init_ceph_lib(void)
 580{
 581        int ret = 0;
 582
 583        ret = ceph_debugfs_init();
 584        if (ret < 0)
 585                goto out;
 586
 587        ret = ceph_crypto_init();
 588        if (ret < 0)
 589                goto out_debugfs;
 590
 591        ret = ceph_msgr_init();
 592        if (ret < 0)
 593                goto out_crypto;
 594
 595        pr_info("loaded (mon/osd proto %d/%d, osdmap %d/%d %d/%d)\n",
 596                CEPH_MONC_PROTOCOL, CEPH_OSDC_PROTOCOL,
 597                CEPH_OSDMAP_VERSION, CEPH_OSDMAP_VERSION_EXT,
 598                CEPH_OSDMAP_INC_VERSION, CEPH_OSDMAP_INC_VERSION_EXT);
 599
 600        return 0;
 601
 602out_crypto:
 603        ceph_crypto_shutdown();
 604out_debugfs:
 605        ceph_debugfs_cleanup();
 606out:
 607        return ret;
 608}
 609
 610static void __exit exit_ceph_lib(void)
 611{
 612        dout("exit_ceph_lib\n");
 613        ceph_msgr_exit();
 614        ceph_crypto_shutdown();
 615        ceph_debugfs_cleanup();
 616}
 617
 618module_init(init_ceph_lib);
 619module_exit(exit_ceph_lib);
 620
 621MODULE_AUTHOR("Sage Weil <sage@newdream.net>");
 622MODULE_AUTHOR("Yehuda Sadeh <yehuda@hq.newdream.net>");
 623MODULE_AUTHOR("Patience Warnick <patience@newdream.net>");
 624MODULE_DESCRIPTION("Ceph filesystem for Linux");
 625MODULE_LICENSE("GPL");
 626