glusterfs

Форк
0
/
glusterd-mountbroker.c 
719 строк · 17.6 Кб
1
/*
2
   Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com>
3
   This file is part of GlusterFS.
4

5
   This file is licensed to you under your choice of the GNU Lesser
6
   General Public License, version 3 or any later version (LGPLv3 or
7
   later), or the GNU General Public License, version 2 (GPLv2), in all
8
   cases as published by the Free Software Foundation.
9
*/
10
#include <inttypes.h>
11
#include <fnmatch.h>
12
#include <pwd.h>
13

14
#include <glusterfs/globals.h>
15
#include <glusterfs/glusterfs.h>
16
#include <glusterfs/compat.h>
17
#include <glusterfs/dict.h>
18
#include <glusterfs/list.h>
19
#include <glusterfs/logging.h>
20
#include <glusterfs/syscall.h>
21
#include <glusterfs/compat.h>
22
#include <glusterfs/compat-errno.h>
23
#include <glusterfs/run.h>
24
#include "glusterd-mem-types.h"
25
#include "glusterd-utils.h"
26
#include <glusterfs/common-utils.h>
27
#include "glusterd-mountbroker.h"
28
#include "glusterd-op-sm.h"
29
#include "glusterd-messages.h"
30

31
static int
32
seq_dict_foreach(dict_t *dict, int (*fn)(char *str, void *data), void *data)
33
{
34
    char index[] = "4294967296";  // 1<<32
35
    int i = 0;
36
    char *val = NULL;
37
    int ret = 0;
38

39
    for (;; i++) {
40
        snprintf(index, sizeof(index), "%d", i);
41
        ret = dict_get_str(dict, index, &val);
42
        if (ret != 0)
43
            return ret == -ENOENT ? 0 : ret;
44
        ret = fn(val, data);
45
        if (ret != 0)
46
            return ret;
47
    }
48
}
49

50
int
51
parse_mount_pattern_desc(gf_mount_spec_t *mspec, char *pdesc)
52
#define SYNTAX_ERR -2
53
{
54
    char *curs = NULL;
55
    char *c2 = NULL;
56
    char sc = '\0';
57
    char **cc = NULL;
58
    gf_mount_pattern_t *pat = NULL;
59
    int pnum = 0;
60
    int ret = 0;
61
    int lastsup = -1;
62
    int incl = -1;
63
    char **pcc = NULL;
64
    int pnc = 0;
65

66
    skipwhite(&pdesc);
67

68
    /* a bow to theory */
69
    if (!*pdesc)
70
        return 0;
71

72
    /* count number of components, separated by '&' */
73
    mspec->len = 0;
74
    for (curs = pdesc; *curs; curs++) {
75
        if (*curs == ')')
76
            mspec->len++;
77
    }
78

79
    mspec->patterns = GF_CALLOC(mspec->len, sizeof(*mspec->patterns),
80
                                gf_gld_mt_mount_pattern);
81
    if (!mspec->patterns) {
82
        gf_smsg("glusterd", GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
83
        ret = -1;
84
        goto out;
85
    }
86

87
    pat = mspec->patterns;
88
    curs = pdesc;
89
    skipwhite(&curs);
90
    for (;;) {
91
        incl = -1;
92

93
        /* check for pattern signedness modifier */
94
        if (*curs == '-') {
95
            pat->negative = _gf_true;
96
            curs++;
97
        }
98

99
        /* now should come condition specifier,
100
         * then opening paren
101
         */
102
        c2 = nwstrtail(curs, "SUB(");
103
        if (c2) {
104
            pat->condition = SET_SUB;
105
            goto got_cond;
106
        }
107
        c2 = nwstrtail(curs, "SUP(");
108
        if (c2) {
109
            pat->condition = SET_SUPER;
110
            lastsup = pat - mspec->patterns;
111
            goto got_cond;
112
        }
113
        c2 = nwstrtail(curs, "EQL(");
114
        if (c2) {
115
            pat->condition = SET_EQUAL;
116
            goto got_cond;
117
        }
118
        c2 = nwstrtail(curs, "MEET(");
119
        if (c2) {
120
            pat->condition = SET_INTERSECT;
121
            goto got_cond;
122
        }
123
        c2 = nwstrtail(curs, "SUB+(");
124
        if (c2) {
125
            pat->condition = SET_SUB;
126
            incl = lastsup;
127
            goto got_cond;
128
        }
129

130
        ret = SYNTAX_ERR;
131
        goto out;
132

133
    got_cond:
134
        curs = c2;
135
        skipwhite(&curs);
136
        /* count the number of components for pattern */
137
        pnum = *curs == ')' ? 0 : 1;
138
        for (c2 = curs; *c2 != ')';) {
139
            if (strchr("&|", *c2)) {
140
                ret = SYNTAX_ERR;
141
                goto out;
142
            }
143
            while (!strchr("|&)", *c2) && !isspace(*c2))
144
                c2++;
145
            skipwhite(&c2);
146
            switch (*c2) {
147
                case ')':
148
                    break;
149
                case '\0':
150
                case '&':
151
                    ret = SYNTAX_ERR;
152
                    goto out;
153
                case '|':
154
                    *c2 = ' ';
155
                    skipwhite(&c2);
156
                    /* fall through */
157
                default:
158
                    pnum++;
159
            }
160
        }
161
        if (incl >= 0) {
162
            pnc = 0;
163
            for (pcc = mspec->patterns[incl].components; *pcc; pcc++)
164
                pnc++;
165
            pnum += pnc;
166
        }
167
        pat->components = GF_CALLOC(pnum + 1, sizeof(*pat->components),
168
                                    gf_gld_mt_mount_comp_container);
169
        if (!pat->components) {
170
            ret = -1;
171
            goto out;
172
        }
173

174
        cc = pat->components;
175
        /* copy over included component set */
176
        if (incl >= 0) {
177
            memcpy(pat->components, mspec->patterns[incl].components,
178
                   pnc * sizeof(*pat->components));
179
            cc += pnc;
180
        }
181
        /* parse and add components */
182
        c2 = ""; /* reset c2 */
183
        while (*c2 != ')') {
184
            c2 = curs;
185
            while (!isspace(*c2) && *c2 != ')')
186
                c2++;
187
            sc = *c2;
188
            *c2 = '\0';
189
            ;
190
            *cc = gf_strdup(curs);
191
            if (!*cc) {
192
                ret = -1;
193
                goto out;
194
            }
195
            *c2 = sc;
196
            skipwhite(&c2);
197
            curs = c2;
198
            cc++;
199
        }
200

201
        curs++;
202
        skipwhite(&curs);
203
        if (*curs == '&') {
204
            curs++;
205
            skipwhite(&curs);
206
        }
207

208
        if (!*curs)
209
            break;
210
        pat++;
211
    }
212

213
out:
214
    if (ret == SYNTAX_ERR) {
215
        gf_msg("glusterd", GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY,
216
               "cannot parse mount patterns %s", pdesc);
217
    }
218

219
    /* We've allocted a lotta stuff here but don't bother with freeing
220
     * on error, in that case we'll terminate anyway
221
     */
222
    return ret ? -1 : 0;
223
}
224
#undef SYNTAX_ERR
225

226
const char *georep_mnt_desc_template =
227
    "SUP("
228
    "aux-gfid-mount "
229
    "acl "
230
    "volfile-server=localhost "
231
    "client-pid=%d "
232
    "user-map-root=%s "
233
    ")"
234
    "SUB+("
235
    "log-file=%s/" GEOREP
236
    "*/* "
237
    "log-level=* "
238
    "volfile-id=* "
239
    ")"
240
    "MEET("
241
    "%s"
242
    ")";
243

244
int
245
make_georep_mountspec(gf_mount_spec_t *mspec, const char *volnames, char *user,
246
                      char *logdir)
247
{
248
    char *georep_mnt_desc = NULL;
249
    char *meetspec = NULL;
250
    char *vols = NULL;
251
    char *vol = NULL;
252
    char *p = NULL;
253
    char *savetok = NULL;
254
    char *fa[3] = {
255
        0,
256
    };
257
    size_t siz = 0;
258
    int vc = 0;
259
    int i = 0;
260
    int ret = 0;
261

262
    vols = gf_strdup((char *)volnames);
263
    if (!vols) {
264
        gf_smsg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_STRDUP_FAILED,
265
                "Volume name=%s", volnames, NULL);
266
        goto out;
267
    }
268

269
    for (vc = 1, p = vols; *p; p++) {
270
        if (*p == ',')
271
            vc++;
272
    }
273
    siz = strlen(volnames) + vc * SLEN("volfile-id=");
274
    meetspec = GF_CALLOC(1, siz + 1, gf_gld_mt_georep_meet_spec);
275
    if (!meetspec) {
276
        gf_smsg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
277
        goto out;
278
    }
279

280
    for (p = vols;;) {
281
        vol = strtok_r(p, ",", &savetok);
282
        if (!vol) {
283
            GF_ASSERT(vc == 0);
284
            break;
285
        }
286
        p = NULL;
287
        strcat(meetspec, "volfile-id=");
288
        strcat(meetspec, vol);
289
        if (--vc > 0)
290
            strcat(meetspec, " ");
291
    }
292

293
    ret = gf_asprintf(&georep_mnt_desc, georep_mnt_desc_template,
294
                      GF_CLIENT_PID_GSYNCD, user, logdir, meetspec);
295
    if (ret == -1) {
296
        georep_mnt_desc = NULL;
297
        goto out;
298
    }
299

300
    ret = parse_mount_pattern_desc(mspec, georep_mnt_desc);
301

302
out:
303
    fa[0] = meetspec;
304
    fa[1] = vols;
305
    fa[2] = georep_mnt_desc;
306

307
    for (i = 0; i < 3; i++) {
308
        if (fa[i] == NULL)
309
            ret = -1;
310
        else
311
            GF_FREE(fa[i]);
312
    }
313

314
    return ret;
315
}
316

317
static gf_boolean_t
318
match_comp(char *str, char *patcomp)
319
{
320
    char *c1 = patcomp;
321
    char *c2 = str;
322

323
    GF_ASSERT(c1);
324
    GF_ASSERT(c2);
325

326
    while (*c1 == *c2) {
327
        if (!*c1)
328
            return _gf_true;
329
        c1++;
330
        c2++;
331
        if (c1[-1] == '=')
332
            break;
333
    }
334

335
    return fnmatch(c1, c2, 0) == 0 ? _gf_true : _gf_false;
336
}
337

338
struct gf_set_descriptor {
339
    gf_boolean_t priv[2];
340
    gf_boolean_t common;
341
};
342

343
static int
344
_gf_set_dict_iter1(char *val, void *data)
345
{
346
    void **dataa = data;
347
    struct gf_set_descriptor *sd = dataa[0];
348
    char **curs = dataa[1];
349
    gf_boolean_t priv = _gf_true;
350

351
    while (*curs) {
352
        if (match_comp(val, *curs)) {
353
            priv = _gf_false;
354
            sd->common = _gf_true;
355
        }
356
        curs++;
357
    }
358

359
    if (priv)
360
        sd->priv[0] = _gf_true;
361

362
    return 0;
363
}
364

365
static int
366
_gf_set_dict_iter2(char *val, void *data)
367
{
368
    void **dataa = data;
369
    gf_boolean_t *boo = dataa[0];
370
    char *comp = dataa[1];
371

372
    if (match_comp(val, comp))
373
        *boo = _gf_true;
374

375
    return 0;
376
}
377

378
static void
379
relate_sets(struct gf_set_descriptor *sd, dict_t *argdict, char **complist)
380
{
381
    void *dataa[] = {NULL, NULL};
382
    gf_boolean_t boo = _gf_false;
383

384
    memset(sd, 0, sizeof(*sd));
385

386
    dataa[0] = sd;
387
    dataa[1] = complist;
388
    seq_dict_foreach(argdict, _gf_set_dict_iter1, dataa);
389

390
    while (*complist) {
391
        boo = _gf_false;
392
        dataa[0] = &boo;
393
        dataa[1] = *complist;
394
        seq_dict_foreach(argdict, _gf_set_dict_iter2, dataa);
395

396
        if (boo)
397
            sd->common = _gf_true;
398
        else
399
            sd->priv[1] = _gf_true;
400

401
        complist++;
402
    }
403
}
404

405
static int
406
_arg_parse_uid(char *val, void *data)
407
{
408
    char *user = strtail(val, "user-map-root=");
409
    struct passwd *pw = NULL;
410

411
    if (!user)
412
        return 0;
413
    pw = getpwnam(user);
414
    if (!pw)
415
        return -EINVAL;
416

417
    if (*(int *)data >= 0)
418
        /* uid ambiguity, already found */
419
        return -EINVAL;
420

421
    *(int *)data = pw->pw_uid;
422
    return 0;
423
}
424

425
static int
426
evaluate_mount_request(xlator_t *this, gf_mount_spec_t *mspec, dict_t *argdict)
427
{
428
    struct gf_set_descriptor sd = {
429
        {
430
            0,
431
        },
432
    };
433
    int i = 0;
434
    int uid = -1;
435
    int ret = 0;
436
    gf_boolean_t match = _gf_false;
437

438
    for (i = 0; i < mspec->len; i++) {
439
        relate_sets(&sd, argdict, mspec->patterns[i].components);
440
        switch (mspec->patterns[i].condition) {
441
            case SET_SUB:
442
                match = !sd.priv[0];
443
                break;
444
            case SET_SUPER:
445
                match = !sd.priv[1];
446
                break;
447
            case SET_EQUAL:
448
                match = (!sd.priv[0] && !sd.priv[1]);
449
                break;
450
            case SET_INTERSECT:
451
                match = sd.common;
452
                break;
453
            default:
454
                GF_ASSERT(!"unreached");
455
        }
456
        if (mspec->patterns[i].negative)
457
            match = !match;
458

459
        if (!match) {
460
            gf_msg(this->name, GF_LOG_ERROR, EPERM,
461
                   GD_MSG_MNTBROKER_SPEC_MISMATCH,
462
                   "Mountbroker spec mismatch!!! SET: %d "
463
                   "COMPONENT: %d. Review the mount args passed",
464
                   mspec->patterns[i].condition, i);
465
            return -EPERM;
466
        }
467
    }
468

469
    ret = seq_dict_foreach(argdict, _arg_parse_uid, &uid);
470
    if (ret != 0)
471
        return ret;
472

473
    return uid;
474
}
475

476
static int
477
_volname_get(char *val, void *data)
478
{
479
    char **volname = data;
480

481
    *volname = strtail(val, "volfile-id=");
482

483
    return *volname ? 1 : 0;
484
}
485

486
static int
487
_runner_add(char *val, void *data)
488
{
489
    runner_t *runner = data;
490

491
    runner_argprintf(runner, "--%s", val);
492

493
    return 0;
494
}
495

496
int
497
glusterd_do_mount(char *label, dict_t *argdict, char **path, int *op_errno)
498
{
499
    glusterd_conf_t *priv = NULL;
500
    char *mountbroker_root = NULL;
501
    gf_mount_spec_t *mspec = NULL;
502
    int uid = -ENOENT;
503
    char *volname = NULL;
504
    glusterd_volinfo_t *vol = NULL;
505
    char *mtptemp = NULL;
506
    char *mntlink = NULL;
507
    char *cookieswitch = NULL;
508
    char *cookie = NULL;
509
    char *sla = NULL;
510
    struct stat st = {
511
        0,
512
    };
513
    runner_t runner = {
514
        0,
515
    };
516
    int ret = 0;
517
    xlator_t *this = THIS;
518
    mode_t orig_umask = 0;
519
    gf_boolean_t found_label = _gf_false;
520

521
    priv = this->private;
522
    GF_ASSERT(priv);
523

524
    GF_ASSERT(op_errno);
525
    *op_errno = 0;
526

527
    if (dict_get_str(this->options, "mountbroker-root", &mountbroker_root) !=
528
        0) {
529
        *op_errno = ENOENT;
530
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
531
               "'option mountbroker-root' "
532
               "missing in glusterd vol file");
533
        goto out;
534
    }
535

536
    GF_ASSERT(label);
537
    if (!*label) {
538
        *op_errno = EINVAL;
539
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_MNTBROKER_LABEL_NULL,
540
               "label is NULL (%s)", strerror(*op_errno));
541
        goto out;
542
    }
543

544
    /* look up spec for label */
545
    cds_list_for_each_entry(mspec, &priv->mount_specs, speclist)
546
    {
547
        if (strcmp(mspec->label, label) != 0)
548
            continue;
549

550
        found_label = _gf_true;
551
        uid = evaluate_mount_request(this, mspec, argdict);
552
        break;
553
    }
554
    if (uid < 0) {
555
        *op_errno = -uid;
556
        if (!found_label) {
557
            gf_msg(this->name, GF_LOG_ERROR, *op_errno,
558
                   GD_MSG_MNTBROKER_LABEL_MISS,
559
                   "Missing mspec: Check the corresponding option "
560
                   "in glusterd vol file for mountbroker user: %s",
561
                   label);
562
        }
563
        goto out;
564
    }
565

566
    /* some sanity check on arguments */
567
    seq_dict_foreach(argdict, _volname_get, &volname);
568
    if (!volname) {
569
        *op_errno = EINVAL;
570
        gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_DICT_GET_FAILED,
571
               "Dict get failed for the key 'volname'");
572
        goto out;
573
    }
574
    if (glusterd_volinfo_find(volname, &vol) != 0 ||
575
        !glusterd_is_volume_started(vol)) {
576
        *op_errno = ENOENT;
577
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_MOUNT_REQ_FAIL,
578
               "Either volume is not started or volinfo not found");
579
        goto out;
580
    }
581

582
    /* go do mount */
583

584
    /** create actual mount dir */
585

586
    /*** "overload" string name to be possible to used for cookie
587
         creation, see below */
588
    ret = gf_asprintf(&mtptemp, "%s/user%d/mtpt-%s-XXXXXX/cookie",
589
                      mountbroker_root, uid, label);
590
    if (ret == -1) {
591
        mtptemp = NULL;
592
        *op_errno = ENOMEM;
593
        goto out;
594
    }
595
    /*** hide cookie part */
596
    cookieswitch = strrchr(mtptemp, '/');
597
    *cookieswitch = '\0';
598

599
    sla = strrchr(mtptemp, '/');
600
    *sla = '\0';
601
    ret = sys_mkdir(mtptemp, 0700);
602
    if (ret == 0)
603
        ret = sys_chown(mtptemp, uid, 0);
604
    else if (errno == EEXIST)
605
        ret = 0;
606
    if (ret == -1) {
607
        *op_errno = errno;
608
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL,
609
               "Mountbroker User directory creation failed");
610
        goto out;
611
    }
612
    ret = sys_lstat(mtptemp, &st);
613
    if (ret == -1) {
614
        *op_errno = errno;
615
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL,
616
               "stat on mountbroker user directory failed");
617
        goto out;
618
    }
619
    if (!(S_ISDIR(st.st_mode) && (st.st_mode & ~S_IFMT) == 0700 &&
620
          st.st_uid == uid && st.st_gid == 0)) {
621
        *op_errno = EACCES;
622
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_MOUNT_REQ_FAIL,
623
               "Incorrect mountbroker user directory attributes");
624
        goto out;
625
    }
626
    *sla = '/';
627

628
    if (!mkdtemp(mtptemp)) {
629
        *op_errno = errno;
630
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL,
631
               "Mountbroker mount directory creation failed");
632
        goto out;
633
    }
634

635
    /** create private "cookie" symlink */
636

637
    /*** occupy an entry in the hive dir via mkstemp */
638
    ret = gf_asprintf(&cookie, "%s/" MB_HIVE "/mntXXXXXX", mountbroker_root);
639
    if (ret == -1) {
640
        cookie = NULL;
641
        *op_errno = ENOMEM;
642
        goto out;
643
    }
644
    orig_umask = umask(S_IRWXG | S_IRWXO);
645
    ret = mkstemp(cookie);
646
    umask(orig_umask);
647
    if (ret == -1) {
648
        *op_errno = errno;
649
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL,
650
               "Mountbroker cookie file creation failed");
651
        goto out;
652
    }
653
    sys_close(ret);
654

655
    /*** assembly the path from cookie to mountpoint */
656
    sla = strchr(sla - 1, '/');
657
    GF_ASSERT(sla);
658
    ret = gf_asprintf(&mntlink, "../user%d%s", uid, sla);
659
    if (ret == -1) {
660
        *op_errno = ENOMEM;
661
        goto out;
662
    }
663

664
    /*** create cookie link in (to-be) mountpoint,
665
         move it over to the final place */
666
    *cookieswitch = '/';
667
    ret = sys_symlink(mntlink, mtptemp);
668
    if (ret != -1)
669
        ret = sys_rename(mtptemp, cookie);
670
    *cookieswitch = '\0';
671
    if (ret == -1) {
672
        *op_errno = errno;
673
        gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL,
674
               "symlink or rename failed");
675
        goto out;
676
    }
677

678
    /** invoke glusterfs on the mountpoint */
679

680
    runinit(&runner);
681
    runner_add_arg(&runner, SBIN_DIR "/glusterfs");
682
    seq_dict_foreach(argdict, _runner_add, &runner);
683
    runner_add_arg(&runner, mtptemp);
684
    ret = runner_run_reuse(&runner);
685
    if (ret == -1) {
686
        *op_errno = EIO; /* XXX hacky fake */
687
        runner_log(&runner, "", GF_LOG_ERROR, "command failed");
688
    }
689
    runner_end(&runner);
690

691
out:
692

693
    if (*op_errno) {
694
        ret = -1;
695
        gf_msg(this->name, GF_LOG_WARNING, *op_errno, GD_MSG_MOUNT_REQ_FAIL,
696
               "unsuccessful mount request");
697
        if (mtptemp) {
698
            *cookieswitch = '/';
699
            sys_unlink(mtptemp);
700
            *cookieswitch = '\0';
701
            sys_rmdir(mtptemp);
702
        }
703
        if (cookie) {
704
            sys_unlink(cookie);
705
            GF_FREE(cookie);
706
        }
707

708
    } else {
709
        ret = 0;
710
        *path = cookie;
711
    }
712

713
    if (mtptemp)
714
        GF_FREE(mtptemp);
715
    if (mntlink)
716
        GF_FREE(mntlink);
717

718
    return ret;
719
}
720

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.