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