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