glusterfs

Форк
0
/
glusterd-snapshot-utils.c 
4230 строк · 132.7 Кб
1
/*
2
   Copyright (c) 2015 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

12
#if defined(GF_LINUX_HOST_OS)
13
#include <mntent.h>
14
#else
15
#include "mntent_compat.h"
16
#endif
17
#include <dlfcn.h>
18

19
#include <glusterfs/dict.h>
20
#include <glusterfs/syscall.h>
21
#include "glusterd-op-sm.h"
22
#include "glusterd-utils.h"
23
#include "glusterd-messages.h"
24
#include "glusterd-store.h"
25
#include "glusterd-volgen.h"
26
#include "glusterd-snapd-svc.h"
27
#include "glusterd-svc-helper.h"
28
#include "glusterd-snapd-svc-helper.h"
29
#include "glusterd-snapshot-utils.h"
30
#include "glusterd-server-quorum.h"
31
#include "glusterd-messages.h"
32
#include "glusterd-errno.h"
33

34
#define GANESHA_EXPORT_DIRECTORY CONFDIR "/exports"
35

36
extern char snap_mount_dir[VALID_GLUSTERD_PATHMAX];
37

38
/*
39
 *  glusterd_snap_geo_rep_restore:
40
 *      This function restores the atime and mtime of marker.tstamp
41
 *      if present from snapped marker.tstamp file.
42
 */
43

44
int32_t
45
glusterd_snapobject_delete(glusterd_snap_t *snap)
46
{
47
    if (snap == NULL) {
48
        gf_msg(THIS->name, GF_LOG_WARNING, 0, GD_MSG_PARAM_NULL,
49
               "snap is NULL");
50
        return -1;
51
    }
52

53
    cds_list_del_init(&snap->snap_list);
54
    cds_list_del_init(&snap->volumes);
55
    if (LOCK_DESTROY(&snap->lock))
56
        gf_msg(THIS->name, GF_LOG_WARNING, 0, GD_MSG_LOCK_DESTROY_FAILED,
57
               "Failed destroying lock"
58
               "of snap %s",
59
               snap->snapname);
60

61
    GF_FREE(snap->description);
62
    GF_FREE(snap);
63

64
    return 0;
65
}
66

67
/*
68
 * This function is to be called only from glusterd_peer_detach_cleanup()
69
 * as this continues to delete snaps in spite of faiure while deleting
70
 * one, as we don't want to fail peer_detach in such a case.
71
 */
72
int
73
glusterd_cleanup_snaps_for_volume(glusterd_volinfo_t *volinfo)
74
{
75
    int32_t op_ret = 0;
76
    int32_t ret = 0;
77
    xlator_t *this = THIS;
78
    glusterd_volinfo_t *snap_vol = NULL;
79
    glusterd_volinfo_t *dummy_snap_vol = NULL;
80
    glusterd_snap_t *snap = NULL;
81

82
    cds_list_for_each_entry_safe(snap_vol, dummy_snap_vol,
83
                                 &volinfo->snap_volumes, snapvol_list)
84
    {
85
        snap = snap_vol->snapshot;
86
        ret = glusterd_store_delete_snap(snap);
87
        if (ret) {
88
            gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_DELETE_FAIL,
89
                   "Failed to remove "
90
                   "snap %s from store",
91
                   snap->snapname);
92
            op_ret = ret;
93
            continue;
94
        }
95

96
        ret = glusterd_snapobject_delete(snap);
97
        if (ret) {
98
            gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_DELETE_FAIL,
99
                   "Failed to delete "
100
                   "snap object %s",
101
                   snap->snapname);
102
            op_ret = ret;
103
            continue;
104
        }
105

106
        ret = glusterd_store_delete_volume(snap_vol);
107
        if (ret) {
108
            gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_DELETE_FAIL,
109
                   "Failed to remove "
110
                   "volume %s from store",
111
                   snap_vol->volname);
112
            op_ret = ret;
113
            continue;
114
        }
115

116
        if (glusterd_volinfo_unref(snap_vol)) {
117
            gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_DELETE_FAIL,
118
                   "Failed to remove "
119
                   "volinfo %s ",
120
                   snap_vol->volname);
121
            op_ret = -1;
122
            continue;
123
        }
124
    }
125

126
    return op_ret;
127
}
128

129
int
130
glusterd_snap_geo_rep_restore(glusterd_volinfo_t *snap_volinfo,
131
                              glusterd_volinfo_t *new_volinfo)
132
{
133
    char vol_tstamp_file[PATH_MAX] = {
134
        0,
135
    };
136
    char snap_tstamp_file[PATH_MAX] = {
137
        0,
138
    };
139
    glusterd_conf_t *priv = NULL;
140
    xlator_t *this = THIS;
141
    int geo_rep_indexing_on = 0;
142
    int ret = 0;
143

144
    GF_ASSERT(snap_volinfo);
145
    GF_ASSERT(new_volinfo);
146

147
    priv = this->private;
148
    GF_ASSERT(priv);
149

150
    /* Check if geo-rep indexing is enabled, if yes, we need restore
151
     * back the mtime of 'marker.tstamp' file.
152
     */
153
    geo_rep_indexing_on = glusterd_volinfo_get_boolean(new_volinfo,
154
                                                       VKEY_MARKER_XTIME);
155
    if (geo_rep_indexing_on == -1) {
156
        gf_msg_debug(this->name, 0,
157
                     "Failed"
158
                     " to check whether geo-rep-indexing enabled or not");
159
        ret = 0;
160
        goto out;
161
    }
162

163
    if (geo_rep_indexing_on == 1) {
164
        GLUSTERD_GET_VOLUME_DIR(vol_tstamp_file, new_volinfo, priv);
165
        strncat(vol_tstamp_file, "/marker.tstamp",
166
                PATH_MAX - strlen(vol_tstamp_file) - 1);
167
        GLUSTERD_GET_VOLUME_DIR(snap_tstamp_file, snap_volinfo, priv);
168
        strncat(snap_tstamp_file, "/marker.tstamp",
169
                PATH_MAX - strlen(snap_tstamp_file) - 1);
170
        ret = gf_set_timestamp(snap_tstamp_file, vol_tstamp_file);
171
        if (ret) {
172
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_TSTAMP_SET_FAIL,
173
                   "Unable to set atime and mtime of %s as of %s",
174
                   vol_tstamp_file, snap_tstamp_file);
175
            goto out;
176
        }
177
    }
178

179
out:
180
    return ret;
181
}
182

183
/* This function will duplicate brickinfo
184
 *
185
 * @param brickinfo     Source brickinfo
186
 * @param dup_brickinfo Destination brickinfo
187
 *
188
 * @return 0 on success else -1
189
 */
190
static int32_t
191
glusterd_brickinfo_dup(glusterd_brickinfo_t *brickinfo,
192
                       glusterd_brickinfo_t *dup_brickinfo)
193
{
194
    int32_t ret = -1;
195
    xlator_t *this = THIS;
196

197
    GF_VALIDATE_OR_GOTO(this->name, brickinfo, out);
198
    GF_VALIDATE_OR_GOTO(this->name, dup_brickinfo, out);
199

200
    strcpy(dup_brickinfo->hostname, brickinfo->hostname);
201
    strcpy(dup_brickinfo->path, brickinfo->path);
202
    strcpy(dup_brickinfo->origin_path, brickinfo->origin_path);
203
    strcpy(dup_brickinfo->real_path, brickinfo->real_path);
204
    strcpy(dup_brickinfo->device_path, brickinfo->device_path);
205
    strcpy(dup_brickinfo->fstype, brickinfo->fstype);
206
    strcpy(dup_brickinfo->mnt_opts, brickinfo->mnt_opts);
207
    ret = gf_canonicalize_path(dup_brickinfo->path);
208
    if (ret) {
209
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_CANONICALIZE_FAIL,
210
               "Failed to canonicalize "
211
               "brick path");
212
        goto out;
213
    }
214
    gf_uuid_copy(dup_brickinfo->uuid, brickinfo->uuid);
215

216
    dup_brickinfo->port = brickinfo->port;
217
    dup_brickinfo->rdma_port = brickinfo->rdma_port;
218
    if (NULL != brickinfo->logfile) {
219
        dup_brickinfo->logfile = gf_strdup(brickinfo->logfile);
220
        if (NULL == dup_brickinfo->logfile) {
221
            ret = -1;
222
            goto out;
223
        }
224
    }
225
    strcpy(dup_brickinfo->brick_id, brickinfo->brick_id);
226
    strcpy(dup_brickinfo->mount_dir, brickinfo->mount_dir);
227
    dup_brickinfo->status = brickinfo->status;
228
    dup_brickinfo->snap_status = brickinfo->snap_status;
229
    dup_brickinfo->snap = brickinfo->snap;
230
out:
231
    return ret;
232
}
233

234
/* This function will copy snap volinfo to the new
235
 * passed volinfo and regenerate backend store files
236
 * for the restored snap.
237
 *
238
 * @param new_volinfo   new volinfo
239
 * @param snap_volinfo  volinfo of snap volume
240
 *
241
 * @return 0 on success and -1 on failure
242
 *
243
 * TODO: Duplicate all members of volinfo, e.g. geo-rep sync secondaries
244
 */
245
int32_t
246
glusterd_snap_volinfo_restore(dict_t *dict, dict_t *rsp_dict,
247
                              glusterd_volinfo_t *new_volinfo,
248
                              glusterd_volinfo_t *snap_volinfo,
249
                              int32_t volcount, gf_boolean_t retain_origin_path,
250
                              char *snap_mount_dir)
251
{
252
    char *value = NULL;
253
    char key[64] = "";
254
    int32_t brick_count = -1;
255
    int32_t ret = -1;
256
    xlator_t *this = THIS;
257
    glusterd_brickinfo_t *brickinfo = NULL;
258
    glusterd_brickinfo_t *new_brickinfo = NULL;
259
    struct glusterd_snap_ops *snap_ops = NULL;
260

261
    GF_ASSERT(dict);
262
    GF_ASSERT(rsp_dict);
263

264
    GF_VALIDATE_OR_GOTO(this->name, new_volinfo, out);
265
    GF_VALIDATE_OR_GOTO(this->name, snap_volinfo, out);
266

267
    brick_count = 0;
268
    cds_list_for_each_entry(brickinfo, &snap_volinfo->bricks, brick_list)
269
    {
270
        brick_count++;
271
        ret = glusterd_brickinfo_new(&new_brickinfo);
272
        if (ret) {
273
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_BRICK_NEW_INFO_FAIL,
274
                   "Failed to create "
275
                   "new brickinfo");
276
            goto out;
277
        }
278

279
        /* Duplicate brickinfo */
280
        ret = glusterd_brickinfo_dup(brickinfo, new_brickinfo);
281
        if (ret) {
282
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_BRICK_SET_INFO_FAIL,
283
                   "Failed to dup "
284
                   "brickinfo");
285
            goto out;
286
        }
287

288
        /* Fetch values if present in dict. These values won't
289
         * be present in case of a missed restore. In that case
290
         * it's fine to use the local node's value
291
         */
292
        if (retain_origin_path) {
293
            snprintf(key, sizeof(key), "snap%d.brick%d.origin_path", volcount,
294
                     brick_count);
295
            ret = dict_get_str(dict, key, &value);
296
            if (!ret)
297
                gf_strncpy(new_brickinfo->path, value,
298
                           sizeof(new_brickinfo->path));
299
        } else {
300
            /* To use generic functions from the plugin */
301
            glusterd_snapshot_plugin_by_name(snap_volinfo->snap_plugin,
302
                                             &snap_ops);
303

304
            snap_ops->brick_path(snap_mount_dir, brickinfo->origin_path, 0,
305
                                 snap_volinfo->snapshot->snapname,
306
                                 snap_volinfo->volname, brickinfo->mount_dir,
307
                                 brick_count - 1, new_brickinfo, 1);
308
        }
309

310
        snprintf(key, sizeof(key), "snap%d.brick%d.snap_status", volcount,
311
                 brick_count);
312
        ret = dict_get_int32(dict, key, &new_brickinfo->snap_status);
313

314
        snprintf(key, sizeof(key), "snap%d.brick%d.device_path", volcount,
315
                 brick_count);
316
        ret = dict_get_str(dict, key, &value);
317
        if (!ret)
318
            gf_strncpy(new_brickinfo->device_path, value,
319
                       sizeof(new_brickinfo->device_path));
320

321
        snprintf(key, sizeof(key), "snap%d.brick%d.fs_type", volcount,
322
                 brick_count);
323
        ret = dict_get_str(dict, key, &value);
324
        if (!ret)
325
            gf_strncpy(new_brickinfo->fstype, value,
326
                       sizeof(new_brickinfo->fstype));
327

328
        snprintf(key, sizeof(key), "snap%d.brick%d.snap_type", volcount,
329
                 brick_count);
330
        ret = dict_get_str(dict, key, &value);
331
        if (!ret)
332
            gf_strncpy(new_brickinfo->snap_type, value,
333
                       sizeof(new_brickinfo->snap_type));
334

335
        snprintf(key, sizeof(key), "snap%d.brick%d.mnt_opts", volcount,
336
                 brick_count);
337
        ret = dict_get_str(dict, key, &value);
338
        if (!ret)
339
            gf_strncpy(new_brickinfo->mnt_opts, value,
340
                       sizeof(new_brickinfo->mnt_opts));
341

342
        /* If the brick is not of this peer, or snapshot is missed *
343
         * for the brick do not replace the xattr for it */
344
        if ((!gf_uuid_compare(brickinfo->uuid, MY_UUID)) &&
345
            (brickinfo->snap_status != -1)) {
346
            /* We need to replace the volume id of all the bricks
347
             * to the volume id of the origin volume. new_volinfo
348
             * has the origin volume's volume id*/
349
            ret = sys_lsetxattr(new_brickinfo->path, GF_XATTR_VOL_ID_KEY,
350
                                new_volinfo->volume_id,
351
                                sizeof(new_volinfo->volume_id), XATTR_REPLACE);
352
            if (ret == -1) {
353
                gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_SET_XATTR_FAIL,
354
                        "Attribute=%s, Path=%s, Reason=%s, Snap=%s",
355
                        GF_XATTR_VOL_ID_KEY, new_brickinfo->path,
356
                        strerror(errno), new_volinfo->volname, NULL);
357
                goto out;
358
            }
359
        }
360

361
        /* If a snapshot is pending for this brick then
362
         * restore should also be pending
363
         */
364
        if (brickinfo->snap_status == -1) {
365
            /* Adding missed delete to the dict */
366
            ret = glusterd_add_missed_snaps_to_dict(
367
                rsp_dict, snap_volinfo, brickinfo, brick_count,
368
                GF_SNAP_OPTION_TYPE_RESTORE);
369
            if (ret) {
370
                gf_msg(this->name, GF_LOG_ERROR, 0,
371
                       GD_MSG_MISSEDSNAP_INFO_SET_FAIL,
372
                       "Failed to add missed snapshot info "
373
                       "for %s:%s in the rsp_dict",
374
                       brickinfo->hostname, brickinfo->path);
375
                goto out;
376
            }
377
        }
378

379
        cds_list_add_tail(&new_brickinfo->brick_list, &new_volinfo->bricks);
380
        /* ownership of new_brickinfo is passed to new_volinfo */
381
        new_brickinfo = NULL;
382
    }
383

384
    /* Regenerate all volfiles */
385
    ret = glusterd_create_volfiles_and_notify_services(new_volinfo);
386
    if (ret) {
387
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLFILE_CREATE_FAIL,
388
               "Failed to regenerate volfiles");
389
        goto out;
390
    }
391

392
    /* Restore geo-rep marker.tstamp's timestamp */
393
    ret = glusterd_snap_geo_rep_restore(snap_volinfo, new_volinfo);
394
    if (ret) {
395
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_TSTAMP_SET_FAIL,
396
               "Geo-rep: marker.tstamp's timestamp restoration failed");
397
        goto out;
398
    }
399

400
out:
401
    if (ret && (NULL != new_brickinfo)) {
402
        (void)glusterd_brickinfo_delete(new_brickinfo);
403
    }
404

405
    return ret;
406
}
407

408
int
409
glusterd_snap_volinfo_find_by_volume_id(uuid_t volume_id,
410
                                        glusterd_volinfo_t **volinfo)
411
{
412
    int32_t ret = -1;
413
    xlator_t *this = THIS;
414
    glusterd_volinfo_t *voliter = NULL;
415
    glusterd_snap_t *snap = NULL;
416
    glusterd_conf_t *priv = NULL;
417

418
    priv = this->private;
419
    GF_ASSERT(priv);
420
    GF_ASSERT(volinfo);
421

422
    if (gf_uuid_is_null(volume_id)) {
423
        gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_UUID_NULL,
424
               "Volume UUID is NULL");
425
        goto out;
426
    }
427

428
    cds_list_for_each_entry(snap, &priv->snapshots, snap_list)
429
    {
430
        cds_list_for_each_entry(voliter, &snap->volumes, vol_list)
431
        {
432
            if (gf_uuid_compare(volume_id, voliter->volume_id))
433
                continue;
434
            *volinfo = voliter;
435
            ret = 0;
436
            goto out;
437
        }
438
    }
439

440
    gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_SNAP_NOT_FOUND,
441
           "Snap volume not found");
442
out:
443
    gf_msg_trace(this->name, 0, "Returning %d", ret);
444
    return ret;
445
}
446

447
int32_t
448
glusterd_snap_volinfo_find(char *snap_volname, glusterd_snap_t *snap,
449
                           glusterd_volinfo_t **volinfo)
450
{
451
    int32_t ret = -1;
452
    xlator_t *this = THIS;
453
    glusterd_volinfo_t *snap_vol = NULL;
454
    glusterd_conf_t *priv = NULL;
455

456
    priv = this->private;
457
    GF_ASSERT(priv);
458
    GF_ASSERT(snap);
459
    GF_ASSERT(snap_volname);
460

461
    cds_list_for_each_entry(snap_vol, &snap->volumes, vol_list)
462
    {
463
        if (!strcmp(snap_vol->volname, snap_volname)) {
464
            ret = 0;
465
            *volinfo = snap_vol;
466
            goto out;
467
        }
468
    }
469

470
    gf_msg(this->name, GF_LOG_WARNING, EINVAL, GD_MSG_SNAP_NOT_FOUND,
471
           "Snap volume %s not found", snap_volname);
472
out:
473
    gf_msg_trace(this->name, 0, "Returning %d", ret);
474
    return ret;
475
}
476

477
int32_t
478
glusterd_snap_volinfo_find_from_parent_volname(char *origin_volname,
479
                                               glusterd_snap_t *snap,
480
                                               glusterd_volinfo_t **volinfo)
481
{
482
    int32_t ret = -1;
483
    xlator_t *this = THIS;
484
    glusterd_volinfo_t *snap_vol = NULL;
485
    glusterd_conf_t *priv = NULL;
486

487
    priv = this->private;
488
    GF_ASSERT(priv);
489
    GF_ASSERT(snap);
490
    GF_ASSERT(origin_volname);
491

492
    cds_list_for_each_entry(snap_vol, &snap->volumes, vol_list)
493
    {
494
        if (!strcmp(snap_vol->parent_volname, origin_volname)) {
495
            ret = 0;
496
            *volinfo = snap_vol;
497
            goto out;
498
        }
499
    }
500

501
    gf_msg_debug(this->name, 0,
502
                 "Snap volume not found(snap: %s, "
503
                 "origin-volume: %s",
504
                 snap->snapname, origin_volname);
505

506
out:
507
    gf_msg_trace(this->name, 0, "Returning %d", ret);
508
    return ret;
509
}
510

511
/* Exports a bricks snapshot details only if required
512
 *
513
 * The details will be exported only if the cluster op-version is greater than
514
 * 4, ie. snapshot is supported in the cluster
515
 */
516
int
517
gd_add_brick_snap_details_to_dict(dict_t *dict, char *prefix,
518
                                  glusterd_brickinfo_t *brickinfo)
519
{
520
    int ret = -1;
521
    xlator_t *this = THIS;
522
    char key[256] = {
523
        0,
524
    };
525

526
    GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out);
527
    GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out);
528
    GF_VALIDATE_OR_GOTO(this->name, (brickinfo != NULL), out);
529

530
    snprintf(key, sizeof(key), "%s.snap_status", prefix);
531
    ret = dict_set_int32(dict, key, brickinfo->snap_status);
532
    if (ret) {
533
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_STATUS_FAIL,
534
               "Failed to set snap_status for %s:%s", brickinfo->hostname,
535
               brickinfo->path);
536
        goto out;
537
    }
538

539
    snprintf(key, sizeof(key), "%s.device_path", prefix);
540
    ret = dict_set_str(dict, key, brickinfo->device_path);
541
    if (ret) {
542
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
543
               "Failed to set snap_device for %s:%s", brickinfo->hostname,
544
               brickinfo->path);
545
        goto out;
546
    }
547

548
    if (brickinfo->origin_path[0] != '\0') {
549
        snprintf(key, sizeof(key), "%s.origin_path", prefix);
550
        ret = dict_set_str(dict, key, brickinfo->origin_path);
551
        if (ret) {
552
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
553
                   "Failed to set origin_path for %s:%s", brickinfo->hostname,
554
                   brickinfo->path);
555
            goto out;
556
        }
557
    }
558

559
    snprintf(key, sizeof(key), "%s.fs_type", prefix);
560
    ret = dict_set_str(dict, key, brickinfo->fstype);
561
    if (ret) {
562
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
563
               "Failed to set fstype for %s:%s", brickinfo->hostname,
564
               brickinfo->path);
565
        goto out;
566
    }
567

568
    snprintf(key, sizeof(key), "%s.snap_type", prefix);
569
    ret = dict_set_str(dict, key, brickinfo->snap_type);
570
    if (ret) {
571
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
572
               "Failed to set snap_type for %s:%s", brickinfo->hostname,
573
               brickinfo->path);
574
        goto out;
575
    }
576

577
    snprintf(key, sizeof(key), "%s.mnt_opts", prefix);
578
    ret = dict_set_str(dict, key, brickinfo->mnt_opts);
579
    if (ret) {
580
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_BRK_MOUNTOPTS_FAIL,
581
               "Failed to set mnt_opts for %s:%s", brickinfo->hostname,
582
               brickinfo->path);
583
        goto out;
584
    }
585

586
    snprintf(key, sizeof(key), "%s.mount_dir", prefix);
587
    ret = dict_set_str(dict, key, brickinfo->mount_dir);
588
    if (ret)
589
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
590
               "Failed to set mount_dir for %s:%s", brickinfo->hostname,
591
               brickinfo->path);
592

593
out:
594
    return ret;
595
}
596

597
/* Exports a volumes snapshot details only if required.
598
 *
599
 * The snapshot details will only be exported if the cluster op-version is
600
 * greater than 4, ie. snapshot is supported in the cluster
601
 */
602
int
603
gd_add_vol_snap_details_to_dict(dict_t *dict, char *prefix,
604
                                glusterd_volinfo_t *volinfo)
605
{
606
    int ret = -1;
607
    xlator_t *this = THIS;
608
    char key[256] = {
609
        0,
610
    };
611

612
    GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out);
613
    GF_VALIDATE_OR_GOTO(this->name, (volinfo != NULL), out);
614
    GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out);
615

616
    snprintf(key, sizeof(key), "%s.restored_from_snap", prefix);
617
    ret = dict_set_dynstr_with_alloc(dict, key,
618
                                     uuid_utoa(volinfo->restored_from_snap));
619
    if (ret) {
620
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
621
               "Unable to set %s for volume"
622
               "%s",
623
               key, volinfo->volname);
624
        goto out;
625
    }
626

627
    if (strlen(volinfo->restored_from_snapname_id) > 0) {
628
        snprintf(key, sizeof(key), "%s.restored_from_snapname_id", prefix);
629
        ret = dict_set_dynstr_with_alloc(dict, key,
630
                                         volinfo->restored_from_snapname_id);
631
        if (ret) {
632
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
633
                   "Unable to set %s for volume"
634
                   "%s",
635
                   key, volinfo->volname);
636
            goto out;
637
        }
638
    }
639

640
    if (strlen(volinfo->restored_from_snapname) > 0) {
641
        snprintf(key, sizeof(key), "%s.restored_from_snapname", prefix);
642
        ret = dict_set_dynstr_with_alloc(dict, key,
643
                                         volinfo->restored_from_snapname);
644
        if (ret) {
645
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
646
                   "Unable to set %s for volume"
647
                   "%s",
648
                   key, volinfo->volname);
649
            goto out;
650
        }
651
    }
652

653
    if (strlen(volinfo->parent_volname) > 0) {
654
        snprintf(key, sizeof(key), "%s.parent_volname", prefix);
655
        ret = dict_set_dynstr_with_alloc(dict, key, volinfo->parent_volname);
656
        if (ret) {
657
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
658
                   "Unable to set %s "
659
                   "for volume %s",
660
                   key, volinfo->volname);
661
            goto out;
662
        }
663
    }
664

665
    snprintf(key, sizeof(key), "%s.is_snap_volume", prefix);
666
    ret = dict_set_uint32(dict, key, volinfo->is_snap_volume);
667
    if (ret) {
668
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
669
               "Unable to set %s for volume"
670
               "%s",
671
               key, volinfo->volname);
672
        goto out;
673
    }
674

675
    snprintf(key, sizeof(key), "%s.snap-max-hard-limit", prefix);
676
    ret = dict_set_uint64(dict, key, volinfo->snap_max_hard_limit);
677
    if (ret) {
678
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
679
               "Unable to set %s for volume"
680
               "%s",
681
               key, volinfo->volname);
682
    }
683

684
    if (strlen(volinfo->snap_plugin) > 0) {
685
        snprintf(key, sizeof(key), "%s.snap_plugin", prefix);
686
        ret = dict_set_dynstr_with_alloc(dict, key, volinfo->snap_plugin);
687
        if (ret) {
688
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
689
                   "Unable to set %s "
690
                   "for volume %s",
691
                   key, volinfo->volname);
692
            goto out;
693
        }
694
    }
695

696
out:
697
    return ret;
698
}
699

700
int32_t
701
glusterd_add_missed_snaps_to_export_dict(dict_t *peer_data)
702
{
703
    char name_buf[PATH_MAX] = "";
704
    char value[PATH_MAX] = "";
705
    int32_t missed_snap_count = 0;
706
    int32_t ret = -1;
707
    glusterd_conf_t *priv = NULL;
708
    glusterd_missed_snap_info *missed_snapinfo = NULL;
709
    glusterd_snap_op_t *snap_opinfo = NULL;
710
    xlator_t *this = THIS;
711

712
    GF_ASSERT(peer_data);
713

714
    priv = this->private;
715
    GF_ASSERT(priv);
716

717
    /* Add the missed_entries in the dict */
718
    cds_list_for_each_entry(missed_snapinfo, &priv->missed_snaps_list,
719
                            missed_snaps)
720
    {
721
        cds_list_for_each_entry(snap_opinfo, &missed_snapinfo->snap_ops,
722
                                snap_ops_list)
723
        {
724
            snprintf(name_buf, sizeof(name_buf), "missed_snaps_%d",
725
                     missed_snap_count);
726
            snprintf(value, sizeof(value), "%s:%s=%s:%d:%s:%d:%d",
727
                     missed_snapinfo->node_uuid, missed_snapinfo->snap_uuid,
728
                     snap_opinfo->snap_vol_id, snap_opinfo->brick_num,
729
                     snap_opinfo->brick_path, snap_opinfo->op,
730
                     snap_opinfo->status);
731

732
            ret = dict_set_dynstr_with_alloc(peer_data, name_buf, value);
733
            if (ret) {
734
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
735
                       "Unable to set %s", name_buf);
736
                goto out;
737
            }
738
            missed_snap_count++;
739
        }
740
    }
741

742
    ret = dict_set_int32(peer_data, "missed_snap_count", missed_snap_count);
743
    if (ret) {
744
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
745
               "Unable to set missed_snap_count");
746
        goto out;
747
    }
748

749
out:
750
    gf_msg_trace(this->name, 0, "Returning %d", ret);
751
    return ret;
752
}
753

754
int32_t
755
glusterd_add_snap_to_dict(glusterd_snap_t *snap, dict_t *peer_data,
756
                          int32_t snap_count)
757
{
758
    char buf[64] = "";
759
    char prefix[32] = "";
760
    int32_t ret = -1;
761
    int32_t volcount = 0;
762
    glusterd_volinfo_t *volinfo = NULL;
763
    glusterd_brickinfo_t *brickinfo = NULL;
764
    gf_boolean_t host_bricks = _gf_false;
765
    xlator_t *this = THIS;
766

767
    GF_ASSERT(snap);
768
    GF_ASSERT(peer_data);
769

770
    snprintf(prefix, sizeof(prefix), "snap%d", snap_count);
771

772
    cds_list_for_each_entry(volinfo, &snap->volumes, vol_list)
773
    {
774
        volcount++;
775
        ret = glusterd_add_volume_to_dict(volinfo, peer_data, volcount, prefix);
776
        if (ret) {
777
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
778
                   "Failed to add snap:%s volume:%s "
779
                   "to peer_data dict for handshake",
780
                   snap->snapname, volinfo->volname);
781
            goto out;
782
        }
783

784
        if (glusterd_is_volume_quota_enabled(volinfo)) {
785
            ret = glusterd_vol_add_quota_conf_to_dict(volinfo, peer_data,
786
                                                      volcount, prefix);
787
            if (ret) {
788
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
789
                       "Failed to add quota conf for "
790
                       "snap:%s volume:%s to peer_data "
791
                       "dict for handshake",
792
                       snap->snapname, volinfo->volname);
793
                goto out;
794
            }
795
        }
796

797
        cds_list_for_each_entry(brickinfo, &volinfo->bricks, brick_list)
798
        {
799
            if (!gf_uuid_compare(brickinfo->uuid, MY_UUID)) {
800
                host_bricks = _gf_true;
801
                break;
802
            }
803
        }
804
    }
805

806
    snprintf(buf, sizeof(buf), "%s.host_bricks", prefix);
807
    ret = dict_set_int8(peer_data, buf, (int8_t)host_bricks);
808
    if (ret) {
809
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
810
               "Unable to set host_bricks for snap %s", snap->snapname);
811
        goto out;
812
    }
813

814
    snprintf(buf, sizeof(buf), "%s.volcount", prefix);
815
    ret = dict_set_int32(peer_data, buf, volcount);
816
    if (ret) {
817
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
818
               "Unable to set volcount for snap %s", snap->snapname);
819
        goto out;
820
    }
821

822
    snprintf(buf, sizeof(buf), "%s.snapname", prefix);
823
    ret = dict_set_dynstr_with_alloc(peer_data, buf, snap->snapname);
824
    if (ret) {
825
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
826
               "Unable to set snapname for snap %s", snap->snapname);
827
        goto out;
828
    }
829

830
    snprintf(buf, sizeof(buf), "%s.snap_id", prefix);
831
    ret = dict_set_dynstr_with_alloc(peer_data, buf, uuid_utoa(snap->snap_id));
832
    if (ret) {
833
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
834
               "Unable to set snap_id for snap %s", snap->snapname);
835
        goto out;
836
    }
837

838
    if (snap->description) {
839
        snprintf(buf, sizeof(buf), "%s.description", prefix);
840
        ret = dict_set_dynstr_with_alloc(peer_data, buf, snap->description);
841
        if (ret) {
842
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
843
                   "Unable to set description for snap %s", snap->snapname);
844
            goto out;
845
        }
846
    }
847

848
    snprintf(buf, sizeof(buf), "%s.time_stamp", prefix);
849
    ret = dict_set_int64(peer_data, buf, (int64_t)snap->time_stamp);
850
    if (ret) {
851
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
852
               "Unable to set time_stamp for snap %s", snap->snapname);
853
        goto out;
854
    }
855

856
    snprintf(buf, sizeof(buf), "%s.snap_restored", prefix);
857
    ret = dict_set_int8(peer_data, buf, snap->snap_restored);
858
    if (ret) {
859
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
860
               "Unable to set snap_restored for snap %s", snap->snapname);
861
        goto out;
862
    }
863

864
    snprintf(buf, sizeof(buf), "%s.snap_status", prefix);
865
    ret = dict_set_int32(peer_data, buf, snap->snap_status);
866
    if (ret) {
867
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
868
               "Unable to set snap_status for snap %s", snap->snapname);
869
        goto out;
870
    }
871
out:
872
    gf_msg_trace(this->name, 0, "Returning %d", ret);
873
    return ret;
874
}
875

876
int32_t
877
glusterd_add_snapshots_to_export_dict(dict_t *peer_data)
878
{
879
    int32_t snap_count = 0;
880
    int32_t ret = -1;
881
    glusterd_conf_t *priv = NULL;
882
    glusterd_snap_t *snap = NULL;
883
    xlator_t *this = THIS;
884

885
    priv = this->private;
886
    GF_ASSERT(priv);
887
    GF_ASSERT(peer_data);
888

889
    cds_list_for_each_entry(snap, &priv->snapshots, snap_list)
890
    {
891
        snap_count++;
892
        ret = glusterd_add_snap_to_dict(snap, peer_data, snap_count);
893
        if (ret) {
894
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
895
                   "Failed to add snap(%s) to the "
896
                   " peer_data dict for handshake",
897
                   snap->snapname);
898
            goto out;
899
        }
900
    }
901

902
    ret = dict_set_int32(peer_data, "snap_count", snap_count);
903
    if (ret) {
904
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
905
               "Failed to set snap_count");
906
        goto out;
907
    }
908

909
out:
910
    gf_msg_trace(this->name, 0, "Returning %d", ret);
911
    return ret;
912
}
913

914
/* Imports the snapshot details of a brick if required and available
915
 *
916
 * Snapshot details will be imported only if the cluster op-version is >= 4
917
 */
918
int
919
gd_import_new_brick_snap_details(dict_t *dict, char *prefix,
920
                                 glusterd_brickinfo_t *brickinfo)
921
{
922
    int ret = -1;
923
    xlator_t *this = THIS;
924
    char key[512] = {
925
        0,
926
    };
927
    char *snap_device = NULL;
928
    char *origin_path = NULL;
929
    char *snap_type = NULL;
930
    char *fs_type = NULL;
931
    char *mnt_opts = NULL;
932
    char *mount_dir = NULL;
933

934
    GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out);
935
    GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out);
936
    GF_VALIDATE_OR_GOTO(this->name, (brickinfo != NULL), out);
937

938
    snprintf(key, sizeof(key), "%s.snap_status", prefix);
939
    ret = dict_get_int32(dict, key, &brickinfo->snap_status);
940
    if (ret) {
941
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
942
               "%s missing in payload", key);
943
        goto out;
944
    }
945

946
    snprintf(key, sizeof(key), "%s.device_path", prefix);
947
    ret = dict_get_str(dict, key, &snap_device);
948
    if (ret) {
949
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
950
               "%s missing in payload", key);
951
        goto out;
952
    }
953
    gf_strncpy(brickinfo->device_path, snap_device,
954
               sizeof(brickinfo->device_path));
955

956
    snprintf(key, sizeof(key), "%s.origin_path", prefix);
957
    ret = dict_get_str(dict, key, &origin_path);
958
    if (ret) {
959
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
960
               "%s missing in payload", key);
961
    } else
962
        gf_strncpy(brickinfo->origin_path, origin_path,
963
                   sizeof(brickinfo->origin_path));
964

965
    snprintf(key, sizeof(key), "%s.fs_type", prefix);
966
    ret = dict_get_str(dict, key, &fs_type);
967
    if (ret) {
968
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
969
               "%s missing in payload", key);
970
        goto out;
971
    }
972
    gf_strncpy(brickinfo->fstype, fs_type, sizeof(brickinfo->fstype));
973

974
    snprintf(key, sizeof(key), "%s.snap_type", prefix);
975
    ret = dict_get_str(dict, key, &snap_type);
976
    if (ret) {
977
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
978
               "%s missing in payload", key);
979
        goto out;
980
    }
981
    gf_strncpy(brickinfo->snap_type, snap_type, sizeof(brickinfo->snap_type));
982

983
    snprintf(key, sizeof(key), "%s.mnt_opts", prefix);
984
    ret = dict_get_str(dict, key, &mnt_opts);
985
    if (ret) {
986
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
987
               "%s missing in payload", key);
988
        goto out;
989
    }
990
    gf_strncpy(brickinfo->mnt_opts, mnt_opts, sizeof(brickinfo->mnt_opts));
991

992
    snprintf(key, sizeof(key), "%s.mount_dir", prefix);
993
    ret = dict_get_str(dict, key, &mount_dir);
994
    if (ret) {
995
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
996
               "%s missing in payload", key);
997
        goto out;
998
    }
999
    gf_strncpy(brickinfo->mount_dir, mount_dir, sizeof(brickinfo->mount_dir));
1000

1001
out:
1002
    return ret;
1003
}
1004

1005
/*
1006
 * Imports the snapshot details of a volume if required and available
1007
 */
1008
int
1009
gd_import_volume_snap_details(dict_t *dict, glusterd_volinfo_t *volinfo,
1010
                              char *prefix, char *volname)
1011
{
1012
    int ret = -1;
1013
    xlator_t *this = THIS;
1014
    char key[256] = {
1015
        0,
1016
    };
1017
    char *restored_snap = NULL;
1018
    char *restored_snapname_id = NULL;
1019
    char *restored_snapname = NULL;
1020
    char *snap_plugin = NULL;
1021

1022
    GF_VALIDATE_OR_GOTO(this->name, (dict != NULL), out);
1023
    GF_VALIDATE_OR_GOTO(this->name, (volinfo != NULL), out);
1024
    GF_VALIDATE_OR_GOTO(this->name, (prefix != NULL), out);
1025
    GF_VALIDATE_OR_GOTO(this->name, (volname != NULL), out);
1026

1027
    snprintf(key, sizeof(key), "%s.is_snap_volume", prefix);
1028
    uint32_t is_snap_int;
1029
    ret = dict_get_uint32(dict, key, &is_snap_int);
1030
    if (ret) {
1031
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1032
               "%s missing in payload "
1033
               "for %s",
1034
               key, volname);
1035
        goto out;
1036
    }
1037
    volinfo->is_snap_volume = (is_snap_int != 0);
1038

1039
    snprintf(key, sizeof(key), "%s.restored_from_snap", prefix);
1040
    ret = dict_get_str(dict, key, &restored_snap);
1041
    if (ret) {
1042
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1043
               "%s missing in payload "
1044
               "for %s",
1045
               key, volname);
1046
        goto out;
1047
    }
1048
    gf_uuid_parse(restored_snap, volinfo->restored_from_snap);
1049

1050
    snprintf(key, sizeof(key), "%s.restored_from_snapname_id", prefix);
1051
    ret = dict_get_str(dict, key, &restored_snapname_id);
1052
    if (ret) {
1053
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1054
               "%s missing in payload "
1055
               "for %s",
1056
               key, volname);
1057
    } else
1058
        gf_strncpy(volinfo->restored_from_snapname_id, restored_snapname_id,
1059
                   sizeof(volinfo->restored_from_snapname_id));
1060

1061
    snprintf(key, sizeof(key), "%s.restored_from_snapname", prefix);
1062
    ret = dict_get_str(dict, key, &restored_snapname);
1063
    if (ret) {
1064
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1065
               "%s missing in payload "
1066
               "for %s",
1067
               key, volname);
1068
    } else
1069
        gf_strncpy(volinfo->restored_from_snapname, restored_snapname,
1070
                   sizeof(volinfo->restored_from_snapname));
1071

1072
    snprintf(key, sizeof(key), "%s.snap_plugin", prefix);
1073
    ret = dict_get_str(dict, key, &snap_plugin);
1074
    if (ret) {
1075
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1076
               "%s missing in payload "
1077
               "for %s",
1078
               key, volname);
1079
    } else
1080
        gf_strncpy(volinfo->snap_plugin, snap_plugin,
1081
                   sizeof(volinfo->snap_plugin));
1082

1083
    snprintf(key, sizeof(key), "%s.snap-max-hard-limit", prefix);
1084
    ret = dict_get_uint64(dict, key, &volinfo->snap_max_hard_limit);
1085
    if (ret)
1086
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1087
               "%s missing in payload "
1088
               "for %s",
1089
               key, volname);
1090
out:
1091
    return ret;
1092
}
1093

1094
int32_t
1095
glusterd_perform_missed_op(glusterd_snap_t *snap, int32_t op)
1096
{
1097
    dict_t *dict = NULL;
1098
    int32_t ret = -1;
1099
    glusterd_conf_t *priv = NULL;
1100
    glusterd_volinfo_t *snap_volinfo = NULL;
1101
    glusterd_volinfo_t *volinfo = NULL;
1102
    glusterd_volinfo_t *tmp = NULL;
1103
    xlator_t *this = THIS;
1104
    uuid_t null_uuid = {0};
1105
    char *parent_volname = NULL;
1106
    gf_boolean_t retain_origin_path = _gf_false;
1107
    glusterd_brickinfo_t *brickinfo = NULL;
1108
    int32_t brick_count = -1;
1109

1110
    priv = this->private;
1111
    GF_ASSERT(priv);
1112
    GF_ASSERT(snap);
1113

1114
    dict = dict_new();
1115
    if (!dict) {
1116
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_CREATE_FAIL,
1117
               "Unable to create dict");
1118
        ret = -1;
1119
        goto out;
1120
    }
1121

1122
    switch (op) {
1123
        case GF_SNAP_OPTION_TYPE_DELETE:
1124
            ret = glusterd_snap_remove(dict, snap, _gf_true, _gf_false,
1125
                                       _gf_false);
1126
            if (ret) {
1127
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_REMOVE_FAIL,
1128
                       "Failed to remove snap");
1129
                goto out;
1130
            }
1131

1132
            break;
1133
        case GF_SNAP_OPTION_TYPE_RESTORE:
1134
            cds_list_for_each_entry_safe(snap_volinfo, tmp, &snap->volumes,
1135
                                         vol_list)
1136
            {
1137
                parent_volname = gf_strdup(snap_volinfo->parent_volname);
1138
                if (!parent_volname)
1139
                    goto out;
1140

1141
                ret = glusterd_volinfo_find(parent_volname, &volinfo);
1142
                if (ret) {
1143
                    gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLINFO_GET_FAIL,
1144
                           "Could not get volinfo of %s", parent_volname);
1145
                    goto out;
1146
                }
1147

1148
                /* Call restore command for each bricks */
1149
                ret = glusterd_bricks_snapshot_restore(dict, snap_volinfo,
1150
                                                       &retain_origin_path);
1151
                if (ret) {
1152
                    gf_msg(this->name, GF_LOG_ERROR, 0,
1153
                           GD_MSG_SNAP_RESTORE_FAIL, "Failed to restore snap");
1154
                    goto out;
1155
                }
1156

1157
                volinfo->version--;
1158
                gf_uuid_copy(volinfo->restored_from_snap, null_uuid);
1159
                strcpy(volinfo->restored_from_snapname_id, "");
1160
                strcpy(volinfo->restored_from_snapname, "");
1161

1162
                /* gd_restore_snap_volume() uses the dict and volcount
1163
                 * to fetch snap brick info from other nodes, which were
1164
                 * collected during prevalidation. As this is an ad-hoc
1165
                 * op and only local node's data matter, hence sending
1166
                 * volcount as 0 and re-using the same dict because we
1167
                 * need not record any missed creates in the rsp_dict.
1168
                 */
1169
                ret = gd_restore_snap_volume(dict, dict, volinfo, snap_volinfo,
1170
                                             0, retain_origin_path);
1171
                if (ret) {
1172
                    gf_msg(this->name, GF_LOG_ERROR, 0,
1173
                           GD_MSG_SNAP_RESTORE_FAIL,
1174
                           "Failed to restore snap for %s", snap->snapname);
1175
                    volinfo->version++;
1176
                    goto out;
1177
                }
1178

1179
                /* Restore is successful therefore delete the original
1180
                 * volume's volinfo. If the volinfo is already restored
1181
                 * then we should delete the backend LVMs */
1182
                if (!gf_uuid_is_null(volinfo->restored_from_snap)) {
1183
                    cds_list_for_each_entry(brickinfo, &volinfo->bricks,
1184
                                            brick_list)
1185
                    {
1186
                        brick_count++;
1187
                        if (gf_uuid_compare(brickinfo->uuid, MY_UUID))
1188
                            continue;
1189

1190
                        ret = glusterd_snapshot_remove(dict, volinfo, brickinfo,
1191
                                                       brick_count);
1192
                        if (ret) {
1193
                            gf_msg(this->name, GF_LOG_ERROR, 0,
1194
                                   GD_MSG_SNAP_REMOVE_FAIL,
1195
                                   "Failed to remove LVM backend");
1196
                            goto out;
1197
                        }
1198
                    }
1199
                }
1200

1201
                /* Detach the volinfo from priv->volumes, so that no new
1202
                 * command can ref it any more and then unref it.
1203
                 */
1204
                cds_list_del_init(&volinfo->vol_list);
1205
                glusterd_volinfo_unref(volinfo);
1206

1207
                ret = glusterd_snapshot_restore_cleanup(dict, parent_volname,
1208
                                                        snap);
1209
                if (ret) {
1210
                    gf_msg(this->name, GF_LOG_ERROR, 0,
1211
                           GD_MSG_SNAP_CLEANUP_FAIL,
1212
                           "Failed to perform snapshot restore "
1213
                           "cleanup for %s volume",
1214
                           parent_volname);
1215
                    goto out;
1216
                }
1217

1218
                GF_FREE(parent_volname);
1219
                parent_volname = NULL;
1220
            }
1221

1222
            break;
1223
        default:
1224
            /* The entry must be a create, delete, or
1225
             * restore entry
1226
             */
1227
            gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY,
1228
                   "Invalid missed snap entry");
1229
            ret = -1;
1230
            goto out;
1231
    }
1232

1233
out:
1234
    dict_unref(dict);
1235
    if (parent_volname) {
1236
        GF_FREE(parent_volname);
1237
        parent_volname = NULL;
1238
    }
1239

1240
    gf_msg_trace(this->name, 0, "Returning %d", ret);
1241
    return ret;
1242
}
1243

1244
/* Perform missed deletes and restores on this node */
1245
int32_t
1246
glusterd_perform_missed_snap_ops(void)
1247
{
1248
    int32_t ret = -1;
1249
    int32_t op_status = -1;
1250
    glusterd_conf_t *priv = NULL;
1251
    glusterd_missed_snap_info *missed_snapinfo = NULL;
1252
    glusterd_snap_op_t *snap_opinfo = NULL;
1253
    glusterd_snap_t *snap = NULL;
1254
    uuid_t snap_uuid = {
1255
        0,
1256
    };
1257
    xlator_t *this = THIS;
1258

1259
    priv = this->private;
1260
    GF_ASSERT(priv);
1261

1262
    cds_list_for_each_entry(missed_snapinfo, &priv->missed_snaps_list,
1263
                            missed_snaps)
1264
    {
1265
        /* If the pending snap_op is not for this node then continue */
1266
        if (strcmp(missed_snapinfo->node_uuid, uuid_utoa(MY_UUID)))
1267
            continue;
1268

1269
        /* Find the snap id */
1270
        gf_uuid_parse(missed_snapinfo->snap_uuid, snap_uuid);
1271
        snap = NULL;
1272
        snap = glusterd_find_snap_by_id(snap_uuid);
1273
        if (!snap) {
1274
            /* If the snap is not found, then a delete or a
1275
             * restore can't be pending on that snap_uuid.
1276
             */
1277
            gf_msg_debug(this->name, 0, "Not a pending delete or restore op");
1278
            continue;
1279
        }
1280

1281
        op_status = GD_MISSED_SNAP_PENDING;
1282
        cds_list_for_each_entry(snap_opinfo, &missed_snapinfo->snap_ops,
1283
                                snap_ops_list)
1284
        {
1285
            /* If the snap_op is create or its status is
1286
             * GD_MISSED_SNAP_DONE then continue
1287
             */
1288
            if ((snap_opinfo->status == GD_MISSED_SNAP_DONE) ||
1289
                (snap_opinfo->op == GF_SNAP_OPTION_TYPE_CREATE))
1290
                continue;
1291

1292
            /* Perform the actual op for the first time for
1293
             * this snap, and mark the snap_status as
1294
             * GD_MISSED_SNAP_DONE. For other entries for the same
1295
             * snap, just mark the entry as done.
1296
             */
1297
            if (op_status == GD_MISSED_SNAP_PENDING) {
1298
                ret = glusterd_perform_missed_op(snap, snap_opinfo->op);
1299
                if (ret) {
1300
                    gf_msg(this->name, GF_LOG_ERROR, 0,
1301
                           GD_MSG_SNAPSHOT_OP_FAILED,
1302
                           "Failed to perform missed snap op");
1303
                    goto out;
1304
                }
1305
                op_status = GD_MISSED_SNAP_DONE;
1306
            }
1307

1308
            snap_opinfo->status = GD_MISSED_SNAP_DONE;
1309
        }
1310
    }
1311

1312
    ret = 0;
1313
out:
1314
    gf_msg_trace(this->name, 0, "Returning %d", ret);
1315
    return ret;
1316
}
1317

1318
/* Import friend volumes missed_snap_list and update *
1319
 * missed_snap_list if need be */
1320
int32_t
1321
glusterd_import_friend_missed_snap_list(dict_t *peer_data)
1322
{
1323
    int32_t missed_snap_count = -1;
1324
    int32_t ret = -1;
1325
    glusterd_conf_t *priv = NULL;
1326
    xlator_t *this = THIS;
1327

1328
    GF_ASSERT(peer_data);
1329

1330
    priv = this->private;
1331
    GF_ASSERT(priv);
1332

1333
    /* Add the friends missed_snaps entries to the in-memory list */
1334
    ret = dict_get_int32(peer_data, "missed_snap_count", &missed_snap_count);
1335
    if (ret) {
1336
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_MISSED_SNAP_GET_FAIL,
1337
               "No missed snaps");
1338
        ret = 0;
1339
        goto out;
1340
    }
1341

1342
    ret = glusterd_add_missed_snaps_to_list(peer_data, missed_snap_count);
1343
    if (ret) {
1344
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_MISSED_SNAP_LIST_STORE_FAIL,
1345
               "Failed to add missed snaps to list");
1346
        goto out;
1347
    }
1348

1349
    ret = glusterd_perform_missed_snap_ops();
1350
    if (ret) {
1351
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAPSHOT_OP_FAILED,
1352
               "Failed to perform snap operations");
1353
        /* Not going to out at this point coz some *
1354
         * missed ops might have been performed. We *
1355
         * need to persist the current list *
1356
         */
1357
    }
1358

1359
    ret = glusterd_store_update_missed_snaps();
1360
    if (ret) {
1361
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_MISSED_SNAP_LIST_STORE_FAIL,
1362
               "Failed to update missed_snaps_list");
1363
        goto out;
1364
    }
1365

1366
out:
1367
    gf_msg_trace(this->name, 0, "Returning %d", ret);
1368
    return ret;
1369
}
1370

1371
/*
1372
 * This function will set boolean "conflict" to true if peer snap
1373
 * has a version greater than snap version of local node. Otherwise
1374
 * boolean "conflict" will be set to false.
1375
 */
1376
int
1377
glusterd_check_peer_has_higher_snap_version(dict_t *peer_data,
1378
                                            char *peer_snap_name, int volcount,
1379
                                            gf_boolean_t *conflict,
1380
                                            char *prefix, glusterd_snap_t *snap,
1381
                                            char *hostname)
1382
{
1383
    glusterd_volinfo_t *snap_volinfo = NULL;
1384
    char key[256] = {0};
1385
    int version = 0, i = 0;
1386
    int ret = 0;
1387
    xlator_t *this = THIS;
1388

1389
    GF_ASSERT(snap);
1390
    GF_ASSERT(peer_data);
1391

1392
    for (i = 1; i <= volcount; i++) {
1393
        snprintf(key, sizeof(key), "%s%d.version", prefix, i);
1394
        ret = dict_get_int32(peer_data, key, &version);
1395
        if (ret) {
1396
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1397
                   "failed to get "
1398
                   "version of snap volume = %s",
1399
                   peer_snap_name);
1400
            return -1;
1401
        }
1402

1403
        /* TODO : As of now there is only one volume in snapshot.
1404
         * Change this when multiple volume snapshot is introduced
1405
         */
1406
        snap_volinfo = cds_list_entry(snap->volumes.next, glusterd_volinfo_t,
1407
                                      vol_list);
1408
        if (!snap_volinfo) {
1409
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLINFO_GET_FAIL,
1410
                   "Failed to get snap "
1411
                   "volinfo %s",
1412
                   snap->snapname);
1413
            return -1;
1414
        }
1415

1416
        if (version > snap_volinfo->version) {
1417
            /* Mismatch detected */
1418
            gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_VOL_VERS_MISMATCH,
1419
                   "Version of volume %s differ. "
1420
                   "local version = %d, remote version = %d "
1421
                   "on peer %s",
1422
                   snap_volinfo->volname, snap_volinfo->version, version,
1423
                   hostname);
1424
            *conflict = _gf_true;
1425
            break;
1426
        } else {
1427
            *conflict = _gf_false;
1428
        }
1429
    }
1430
    return 0;
1431
}
1432

1433
/* Check for the peer_snap_name in the list of existing snapshots.
1434
 * If a snap exists with the same name and a different snap_id, then
1435
 * there is a conflict. Set conflict as _gf_true, and snap to the
1436
 * conflicting snap object. If a snap exists with the same name, and the
1437
 * same snap_id, then there is no conflict. Set conflict as _gf_false
1438
 * and snap to the existing snap object. If no snap exists with the
1439
 * peer_snap_name, then there is no conflict. Set conflict as _gf_false
1440
 * and snap to NULL.
1441
 */
1442
void
1443
glusterd_is_peer_snap_conflicting(char *peer_snap_name, char *peer_snap_id,
1444
                                  gf_boolean_t *conflict,
1445
                                  glusterd_snap_t **snap, char *hostname)
1446
{
1447
    uuid_t peer_snap_uuid = {
1448
        0,
1449
    };
1450
    xlator_t *this = THIS;
1451

1452
    GF_ASSERT(peer_snap_name);
1453
    GF_ASSERT(peer_snap_id);
1454
    GF_ASSERT(conflict);
1455
    GF_ASSERT(snap);
1456
    GF_ASSERT(hostname);
1457

1458
    *snap = glusterd_find_snap_by_name(peer_snap_name);
1459
    if (*snap) {
1460
        gf_uuid_parse(peer_snap_id, peer_snap_uuid);
1461
        if (!gf_uuid_compare(peer_snap_uuid, (*snap)->snap_id)) {
1462
            /* Current node contains the same snap having
1463
             * the same snapname and snap_id
1464
             */
1465
            gf_msg_debug(this->name, 0,
1466
                         "Snapshot %s from peer %s present in "
1467
                         "localhost",
1468
                         peer_snap_name, hostname);
1469
            *conflict = _gf_false;
1470
        } else {
1471
            /* Current node contains the same snap having
1472
             * the same snapname but different snap_id
1473
             */
1474
            gf_msg_debug(this->name, 0,
1475
                         "Snapshot %s from peer %s conflicts with "
1476
                         "snapshot in localhost",
1477
                         peer_snap_name, hostname);
1478
            *conflict = _gf_true;
1479
        }
1480
    } else {
1481
        /* Peer contains snapshots missing on the current node */
1482
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_MISSED_SNAP_PRESENT,
1483
               "Snapshot %s from peer %s missing on localhost", peer_snap_name,
1484
               hostname);
1485
        *conflict = _gf_false;
1486
    }
1487
}
1488

1489
/* Check if the local node is hosting any bricks for the given snapshot */
1490
gf_boolean_t
1491
glusterd_are_snap_bricks_local(glusterd_snap_t *snap)
1492
{
1493
    gf_boolean_t is_local = _gf_false;
1494
    glusterd_volinfo_t *volinfo = NULL;
1495
    glusterd_brickinfo_t *brickinfo = NULL;
1496

1497
    GF_ASSERT(snap);
1498

1499
    cds_list_for_each_entry(volinfo, &snap->volumes, vol_list)
1500
    {
1501
        cds_list_for_each_entry(brickinfo, &volinfo->bricks, brick_list)
1502
        {
1503
            if (!gf_uuid_compare(brickinfo->uuid, MY_UUID)) {
1504
                is_local = _gf_true;
1505
                goto out;
1506
            }
1507
        }
1508
    }
1509

1510
out:
1511
    gf_msg_trace(THIS->name, 0, "Returning %d", is_local);
1512
    return is_local;
1513
}
1514

1515
/* Check if the peer has missed any snap delete
1516
 * or restore for the given snap_id
1517
 */
1518
gf_boolean_t
1519
glusterd_peer_has_missed_snap_delete(uuid_t peerid, char *peer_snap_id)
1520
{
1521
    char *peer_uuid = NULL;
1522
    gf_boolean_t missed_delete = _gf_false;
1523
    glusterd_conf_t *priv = NULL;
1524
    glusterd_missed_snap_info *missed_snapinfo = NULL;
1525
    glusterd_snap_op_t *snap_opinfo = NULL;
1526
    xlator_t *this = THIS;
1527

1528
    priv = this->private;
1529
    GF_ASSERT(priv);
1530
    GF_ASSERT(peer_snap_id);
1531

1532
    peer_uuid = uuid_utoa(peerid);
1533

1534
    cds_list_for_each_entry(missed_snapinfo, &priv->missed_snaps_list,
1535
                            missed_snaps)
1536
    {
1537
        /* Look for missed snap for the same peer, and
1538
         * the same snap_id
1539
         */
1540
        if ((!strcmp(peer_uuid, missed_snapinfo->node_uuid)) &&
1541
            (!strcmp(peer_snap_id, missed_snapinfo->snap_uuid))) {
1542
            /* Check if the missed snap's op is delete and the
1543
             * status is pending
1544
             */
1545
            cds_list_for_each_entry(snap_opinfo, &missed_snapinfo->snap_ops,
1546
                                    snap_ops_list)
1547
            {
1548
                if (((snap_opinfo->op == GF_SNAP_OPTION_TYPE_DELETE) ||
1549
                     (snap_opinfo->op == GF_SNAP_OPTION_TYPE_RESTORE)) &&
1550
                    (snap_opinfo->status == GD_MISSED_SNAP_PENDING)) {
1551
                    missed_delete = _gf_true;
1552
                    goto out;
1553
                }
1554
            }
1555
        }
1556
    }
1557

1558
out:
1559
    gf_msg_trace(this->name, 0, "Returning %d", missed_delete);
1560
    return missed_delete;
1561
}
1562

1563
/* Generate and store snap volfiles for imported snap object */
1564
int32_t
1565
glusterd_gen_snap_volfiles(glusterd_volinfo_t *snap_vol, char *peer_snap_name)
1566
{
1567
    int32_t ret = -1;
1568
    xlator_t *this = THIS;
1569
    glusterd_volinfo_t *parent_volinfo = NULL;
1570

1571
    GF_ASSERT(snap_vol);
1572
    GF_ASSERT(peer_snap_name);
1573

1574
    ret = glusterd_store_volinfo(snap_vol, GLUSTERD_VOLINFO_VER_AC_NONE);
1575
    if (ret) {
1576
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLINFO_SET_FAIL,
1577
               "Failed to store snapshot "
1578
               "volinfo (%s) for snap %s",
1579
               snap_vol->volname, peer_snap_name);
1580
        goto out;
1581
    }
1582

1583
    ret = generate_brick_volfiles(snap_vol);
1584
    if (ret) {
1585
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLFILE_CREATE_FAIL,
1586
               "generating the brick volfiles for the "
1587
               "snap %s failed",
1588
               peer_snap_name);
1589
        goto out;
1590
    }
1591

1592
    ret = generate_client_volfiles(snap_vol, GF_CLIENT_TRUSTED);
1593
    if (ret) {
1594
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLFILE_CREATE_FAIL,
1595
               "generating the trusted client volfiles for "
1596
               "the snap %s failed",
1597
               peer_snap_name);
1598
        goto out;
1599
    }
1600

1601
    ret = generate_client_volfiles(snap_vol, GF_CLIENT_OTHER);
1602
    if (ret) {
1603
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLFILE_CREATE_FAIL,
1604
               "generating the client volfiles for the "
1605
               "snap %s failed",
1606
               peer_snap_name);
1607
        goto out;
1608
    }
1609

1610
    ret = glusterd_volinfo_find(snap_vol->parent_volname, &parent_volinfo);
1611
    if (ret) {
1612
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLINFO_GET_FAIL,
1613
               "Parent volinfo "
1614
               "not found for %s volume of snap %s",
1615
               snap_vol->volname, peer_snap_name);
1616
        goto out;
1617
    }
1618

1619
    glusterd_list_add_snapvol(parent_volinfo, snap_vol);
1620

1621
    ret = glusterd_store_volinfo(snap_vol, GLUSTERD_VOLINFO_VER_AC_NONE);
1622
    if (ret) {
1623
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLINFO_SET_FAIL,
1624
               "Failed to store snap volinfo");
1625
        goto out;
1626
    }
1627
out:
1628
    gf_msg_trace(this->name, 0, "Returning %d", ret);
1629
    return ret;
1630
}
1631

1632
/* Import snapshot info from peer_data and add it to priv */
1633
int32_t
1634
glusterd_import_friend_snap(dict_t *peer_data, int32_t snap_count,
1635
                            char *peer_snap_name, char *peer_snap_id)
1636
{
1637
    char buf[64] = "";
1638
    char prefix[32] = "";
1639
    char *description = NULL;
1640
    dict_t *dict = NULL;
1641
    glusterd_snap_t *snap = NULL;
1642
    glusterd_volinfo_t *snap_vol = NULL;
1643
    glusterd_conf_t *priv = NULL;
1644
    int32_t ret = -1;
1645
    int32_t volcount = -1;
1646
    int32_t i = -1;
1647
    xlator_t *this = THIS;
1648
    int64_t time_stamp;
1649

1650
    priv = this->private;
1651
    GF_ASSERT(priv);
1652
    GF_ASSERT(peer_data);
1653
    GF_ASSERT(peer_snap_name);
1654
    GF_ASSERT(peer_snap_id);
1655

1656
    snprintf(prefix, sizeof(prefix), "snap%d", snap_count);
1657

1658
    snap = glusterd_new_snap_object();
1659
    if (!snap) {
1660
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_CREATION_FAIL,
1661
               "Could not create "
1662
               "the snap object for snap %s",
1663
               peer_snap_name);
1664
        goto out;
1665
    }
1666

1667
    dict = dict_new();
1668
    if (!dict) {
1669
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_CREATE_FAIL,
1670
               "Failed to create dict");
1671
        ret = -1;
1672
        goto out;
1673
    }
1674

1675
    gf_strncpy(snap->snapname, peer_snap_name, sizeof(snap->snapname));
1676
    gf_uuid_parse(peer_snap_id, snap->snap_id);
1677

1678
    snprintf(buf, sizeof(buf), "%s.description", prefix);
1679
    ret = dict_get_str(peer_data, buf, &description);
1680
    if (ret == 0 && description) {
1681
        snap->description = gf_strdup(description);
1682
        if (snap->description == NULL) {
1683
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_CREATION_FAIL,
1684
                   "Saving the Snapshot Description Failed");
1685
            ret = -1;
1686
            goto out;
1687
        }
1688
    }
1689

1690
    snprintf(buf, sizeof(buf), "%s.time_stamp", prefix);
1691
    ret = dict_get_int64(peer_data, buf, &time_stamp);
1692
    if (ret) {
1693
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1694
               "Unable to get time_stamp for snap %s", peer_snap_name);
1695
        goto out;
1696
    }
1697
    snap->time_stamp = (time_t)time_stamp;
1698

1699
    snprintf(buf, sizeof(buf), "%s.snap_restored", prefix);
1700
    ret = dict_get_int8(peer_data, buf, (int8_t *)&snap->snap_restored);
1701
    if (ret) {
1702
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1703
               "Unable to get snap_restored for snap %s", peer_snap_name);
1704
        goto out;
1705
    }
1706

1707
    snprintf(buf, sizeof(buf), "%s.snap_status", prefix);
1708
    ret = dict_get_int32(peer_data, buf, (int32_t *)&snap->snap_status);
1709
    if (ret) {
1710
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1711
               "Unable to get snap_status for snap %s", peer_snap_name);
1712
        goto out;
1713
    }
1714

1715
    /* If the snap is scheduled to be decommissioned, then
1716
     * don't accept the snap */
1717
    if (snap->snap_status == GD_SNAP_STATUS_DECOMMISSION) {
1718
        gf_msg_debug(this->name, 0,
1719
                     "The snap(%s) is scheduled to be decommissioned "
1720
                     "Not accepting the snap.",
1721
                     peer_snap_name);
1722
        glusterd_snap_remove(dict, snap, _gf_true, _gf_true, _gf_false);
1723
        ret = 0;
1724
        goto out;
1725
    }
1726

1727
    snprintf(buf, sizeof(buf), "%s.volcount", prefix);
1728
    ret = dict_get_int32(peer_data, buf, &volcount);
1729
    if (ret) {
1730
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1731
               "Unable to get volcount for snap %s", peer_snap_name);
1732
        goto out;
1733
    }
1734

1735
    ret = glusterd_store_create_snap_dir(snap);
1736
    if (ret) {
1737
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAPDIR_CREATE_FAIL,
1738
               "Failed to create snap dir");
1739
        goto out;
1740
    }
1741

1742
    glusterd_list_add_order(&snap->snap_list, &priv->snapshots,
1743
                            glusterd_compare_snap_time);
1744

1745
    for (i = 1; i <= volcount; i++) {
1746
        ret = glusterd_import_volinfo(peer_data, i, &snap_vol, prefix);
1747
        if (ret) {
1748
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLINFO_SET_FAIL,
1749
                   "Failed to import snap volinfo for "
1750
                   "snap %s",
1751
                   peer_snap_name);
1752
            goto out;
1753
        }
1754

1755
        snap_vol->snapshot = snap;
1756

1757
        ret = glusterd_gen_snap_volfiles(snap_vol, peer_snap_name);
1758
        if (ret) {
1759
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLFILE_CREATE_FAIL,
1760
                   "Failed to generate snap vol files "
1761
                   "for snap %s",
1762
                   peer_snap_name);
1763
            goto out;
1764
        }
1765
        /* During handshake, after getting updates from friend mount
1766
         * point for activated snapshot should exist and should not
1767
         * for deactivated snapshot.
1768
         */
1769
        if (glusterd_is_volume_started(snap_vol)) {
1770
            ret = glusterd_recreate_vol_brick_mounts(this, snap_vol);
1771
            if (ret) {
1772
                gf_msg(this->name, GF_LOG_ERROR, 0,
1773
                       GD_MSG_BRK_MNT_RECREATE_FAIL,
1774
                       "Failed to recreate brick mounts"
1775
                       " for %s",
1776
                       snap->snapname);
1777
                goto out;
1778
            }
1779

1780
            (void)glusterd_start_bricks(snap_vol);
1781
            ret = glusterd_store_volinfo(snap_vol,
1782
                                         GLUSTERD_VOLINFO_VER_AC_NONE);
1783
            if (ret) {
1784
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOLINFO_STORE_FAIL,
1785
                       "Failed to "
1786
                       "write volinfo for volume %s",
1787
                       snap_vol->volname);
1788
                goto out;
1789
            }
1790
        } else {
1791
            (void)glusterd_stop_bricks(snap_vol);
1792
            ret = glusterd_snap_unmount(this, snap_vol);
1793
            if (ret) {
1794
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_GLUSTERD_UMOUNT_FAIL,
1795
                       "Failed to unmounts for %s", snap->snapname);
1796
            }
1797
        }
1798

1799
        ret = glusterd_import_quota_conf(peer_data, i, snap_vol, prefix);
1800
        if (ret) {
1801
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_QUOTA_CONFIG_IMPORT_FAIL,
1802
                   "Failed to import quota conf "
1803
                   "for snap %s",
1804
                   peer_snap_name);
1805
            goto out;
1806
        }
1807

1808
        snap_vol = NULL;
1809
    }
1810

1811
    ret = glusterd_store_snap(snap);
1812
    if (ret) {
1813
        gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_SNAP_CREATION_FAIL,
1814
               "Could not store snap"
1815
               "object %s",
1816
               peer_snap_name);
1817
        goto out;
1818
    }
1819
    glusterd_fetchsnap_notify(this);
1820

1821
out:
1822
    if (ret)
1823
        glusterd_snap_remove(dict, snap, _gf_true, _gf_true, _gf_false);
1824

1825
    if (dict)
1826
        dict_unref(dict);
1827

1828
    gf_msg_trace(this->name, 0, "Returning %d", ret);
1829
    return ret;
1830
}
1831

1832
/* During a peer-handshake, after the volumes have synced, and the list of
1833
 * missed snapshots have synced, the node will perform the pending deletes
1834
 * and restores on this list. At this point, the current snapshot list in
1835
 * the node will be updated, and hence in case of conflicts arising during
1836
 * snapshot handshake, the peer hosting the bricks will be given precedence
1837
 * Likewise, if there will be a conflict, and both peers will be in the same
1838
 * state, i.e either both would be hosting bricks or both would not be hosting
1839
 * bricks, then a decision can't be taken and a peer-reject will happen.
1840
 *
1841
 * glusterd_compare_snap()  & glusterd_update_snaps () implement the following
1842
 * algorithm to perform the above task. Please note the former function tries to
1843
 * iterate over the snaps one at a time and updating the relevant fields in the
1844
 * dictionary and then glusterd_update_snaps () go over all the snaps and update
1845
 * them at one go as part of a synctask.
1846
 * Step  1: Start.
1847
 * Step  2: Check if the peer is missing a delete or restore on the said snap.
1848
 *          If yes, goto step 6.
1849
 * Step  3: Check if there is a conflict between the peer's data and the
1850
 *          local snap. If no, goto step 5.
1851
 * Step  4: As there is a conflict, check if both the peer and the local nodes
1852
 *          are hosting bricks. Based on the results perform the following:
1853
 *          Peer Hosts Bricks    Local Node Hosts Bricks       Action
1854
 *                Yes                     Yes                Goto Step 8
1855
 *                No                      No                 Goto Step 8
1856
 *                Yes                     No                 Goto Step 9
1857
 *                No                      Yes                Goto Step 7
1858
 * Step  5: Check if the local node is missing the peer's data.
1859
 *          If yes, goto step 10.
1860
 * Step  6: Check if the snap volume version is lesser than peer_data
1861
 *          if yes goto step 9
1862
 * Step  7: It's a no-op. Goto step 11
1863
 * Step  8: Peer Reject. Goto step 11
1864
 * Step  9: Delete local node's data.
1865
 * Step 10: Accept Peer Data.
1866
 * Step 11: Stop
1867
 *
1868
 */
1869
int32_t
1870
glusterd_compare_snap(dict_t *peer_data, int32_t snap_count, char *peername,
1871
                      uuid_t peerid)
1872
{
1873
    char buf[64] = "";
1874
    char prefix[32] = "";
1875
    char *peer_snap_name = NULL;
1876
    char *peer_snap_id = NULL;
1877
    glusterd_snap_t *snap = NULL;
1878
    gf_boolean_t conflict = _gf_false;
1879
    gf_boolean_t is_local = _gf_false;
1880
    gf_boolean_t is_hosted = _gf_false;
1881
    gf_boolean_t missed_delete = _gf_false;
1882
    int32_t ret = -1;
1883
    int32_t volcount = 0;
1884
    xlator_t *this = THIS;
1885

1886
    GF_ASSERT(peer_data);
1887
    GF_ASSERT(peername);
1888

1889
    snprintf(prefix, sizeof(prefix), "snap%d", snap_count);
1890

1891
    ret = dict_set_uint32(peer_data, buf, 0);
1892
    snprintf(buf, sizeof(buf), "%s.accept_peer_data", prefix);
1893
    ret = dict_set_uint32(peer_data, buf, 0);
1894
    snprintf(buf, sizeof(buf), "%s.remove_lvm", prefix);
1895
    ret = dict_set_uint32(peer_data, buf, 0);
1896
    snprintf(buf, sizeof(buf), "%s.remove_my_data", prefix);
1897
    ret = dict_set_uint32(peer_data, buf, 0);
1898

1899
    /* Fetch the peer's snapname */
1900
    snprintf(buf, sizeof(buf), "%s.snapname", prefix);
1901
    ret = dict_get_str(peer_data, buf, &peer_snap_name);
1902
    if (ret) {
1903
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1904
               "Unable to fetch snapname from peer: %s", peername);
1905
        goto out;
1906
    }
1907

1908
    /* Fetch the peer's snap_id */
1909
    snprintf(buf, sizeof(buf), "%s.snap_id", prefix);
1910
    ret = dict_get_str(peer_data, buf, &peer_snap_id);
1911
    if (ret) {
1912
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1913
               "Unable to fetch snap_id from peer: %s", peername);
1914
        goto out;
1915
    }
1916

1917
    snprintf(buf, sizeof(buf), "%s.volcount", prefix);
1918
    ret = dict_get_int32(peer_data, buf, &volcount);
1919
    if (ret) {
1920
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
1921
               "Unable to get volcount for snap %s", peer_snap_name);
1922
        goto out;
1923
    }
1924

1925
    /* Check if the peer has missed a snap delete or restore
1926
     * resulting in stale data for the snap in question
1927
     */
1928
    missed_delete = glusterd_peer_has_missed_snap_delete(peerid, peer_snap_id);
1929
    if (missed_delete == _gf_true) {
1930
        /* Peer has missed delete on the missing/conflicting snap_id */
1931
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_MISSED_SNAP_DELETE,
1932
               "Peer %s has missed a delete "
1933
               "on snap %s",
1934
               peername, peer_snap_name);
1935
        ret = 0;
1936
        goto out;
1937
    }
1938

1939
    /* Check if there is a conflict, and if the
1940
     * peer data is already present
1941
     */
1942
    glusterd_is_peer_snap_conflicting(peer_snap_name, peer_snap_id, &conflict,
1943
                                      &snap, peername);
1944
    if (conflict == _gf_false) {
1945
        if (!snap) {
1946
            /* Peer has snap with the same snapname
1947
             * and snap_id, which local node doesn't have.
1948
             */
1949
            snprintf(buf, sizeof(buf), "%s.accept_peer_data", prefix);
1950
            ret = dict_set_uint32(peer_data, buf, 1);
1951
            goto out;
1952
        }
1953
        /* Peer has snap with the same snapname
1954
         * and snap_id. Now check if peer has a
1955
         * snap with higher snap version than local
1956
         * node has.
1957
         */
1958
        ret = glusterd_check_peer_has_higher_snap_version(
1959
            peer_data, peer_snap_name, volcount, &conflict, prefix, snap,
1960
            peername);
1961
        if (ret) {
1962
            gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_VERS_MISMATCH,
1963
                   "Failed "
1964
                   "to check version of snap volume");
1965
            goto out;
1966
        }
1967
        if (conflict == _gf_true) {
1968
            /*
1969
             * Snap version of peer is higher than snap
1970
             * version of local node.
1971
             *
1972
             * Remove data in local node and accept peer data.
1973
             * We just need to heal snap info of local node, So
1974
             * When removing data from local node, make sure
1975
             * we are not removing backend lvm of the snap.
1976
             */
1977
            snprintf(buf, sizeof(buf), "%s.remove_lvm", prefix);
1978
            ret = dict_set_uint32(peer_data, buf, 0);
1979
            snprintf(buf, sizeof(buf), "%s.remove_my_data", prefix);
1980
            ret = dict_set_uint32(peer_data, buf, 1);
1981
            snprintf(buf, sizeof(buf), "%s.accept_peer_data", prefix);
1982
            ret = dict_set_uint32(peer_data, buf, 1);
1983

1984
        } else {
1985
            ret = 0;
1986
        }
1987
        goto out;
1988
    }
1989

1990
    /* There is a conflict. Check if the current node is
1991
     * hosting bricks for the conflicted snap.
1992
     */
1993
    is_local = glusterd_are_snap_bricks_local(snap);
1994

1995
    /* Check if the peer is hosting any bricks for the
1996
     * conflicting snap
1997
     */
1998
    snprintf(buf, sizeof(buf), "%s.host_bricks", prefix);
1999
    ret = dict_get_int8(peer_data, buf, (int8_t *)&is_hosted);
2000
    if (ret) {
2001
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2002
               "Unable to fetch host_bricks from peer: %s "
2003
               "for %s",
2004
               peername, peer_snap_name);
2005
        goto out;
2006
    }
2007

2008
    /* As there is a conflict at this point of time, the data of the
2009
     * node that hosts a brick takes precedence. If both the local
2010
     * node and the peer are in the same state, i.e if both of them
2011
     * are either hosting or not hosting the bricks, for the snap,
2012
     * then it's a peer reject
2013
     */
2014
    if (is_hosted == is_local) {
2015
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_CONFLICT,
2016
               "Conflict in snapshot %s with peer %s", peer_snap_name,
2017
               peername);
2018
        ret = -1;
2019
        goto out;
2020
    }
2021

2022
    if (is_hosted == _gf_false) {
2023
        /* If there was a conflict, and the peer is not hosting
2024
         * any brick, then don't accept peer data
2025
         */
2026
        gf_msg_debug(this->name, 0,
2027
                     "Peer doesn't hosts bricks for conflicting "
2028
                     "snap(%s). Not accepting peer data.",
2029
                     peer_snap_name);
2030
        ret = 0;
2031
        goto out;
2032
    }
2033

2034
    /* The peer is hosting a brick in case of conflict
2035
     * And local node isn't. Hence remove local node's
2036
     * data and accept peer data
2037
     */
2038
    gf_msg_debug(this->name, 0,
2039
                 "Peer hosts bricks for conflicting "
2040
                 "snap(%s). Removing local data. Accepting peer data.",
2041
                 peer_snap_name);
2042
    snprintf(buf, sizeof(buf), "%s.remove_lvm", prefix);
2043
    ret = dict_set_uint32(peer_data, buf, 1);
2044
    snprintf(buf, sizeof(buf), "%s.remove_my_data", prefix);
2045
    ret = dict_set_uint32(peer_data, buf, 1);
2046
    snprintf(buf, sizeof(buf), "%s.accept_peer_data", prefix);
2047
    ret = dict_set_uint32(peer_data, buf, 1);
2048

2049
out:
2050
    gf_msg_trace(this->name, 0, "Returning %d", ret);
2051
    return ret;
2052
}
2053

2054
int32_t
2055
glusterd_update_snaps_synctask(void *opaque)
2056
{
2057
    int32_t ret = -1;
2058
    int32_t snap_count = 0;
2059
    int i = 1;
2060
    xlator_t *this = THIS;
2061
    dict_t *peer_data = NULL;
2062
    char buf[64] = "";
2063
    char prefix[32] = "";
2064
    char *peer_snap_name = NULL;
2065
    char *peer_snap_id = NULL;
2066
    char *peername = NULL;
2067
    gf_boolean_t remove_lvm = _gf_false;
2068
    gf_boolean_t remove_my_data = _gf_false;
2069
    gf_boolean_t accept_peer_data = _gf_false;
2070
    int32_t val = 0;
2071
    glusterd_snap_t *snap = NULL;
2072
    dict_t *dict = NULL;
2073
    glusterd_conf_t *conf = NULL;
2074

2075
    conf = this->private;
2076
    GF_ASSERT(conf);
2077

2078
    peer_data = (dict_t *)opaque;
2079
    GF_ASSERT(peer_data);
2080

2081
    synclock_lock(&conf->big_lock);
2082

2083
    while (conf->restart_bricks) {
2084
        synccond_wait(&conf->cond_restart_bricks, &conf->big_lock);
2085
    }
2086
    conf->restart_bricks = _gf_true;
2087

2088
    ret = dict_get_int32(peer_data, "snap_count", &snap_count);
2089
    if (ret) {
2090
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2091
               "Failed to fetch snap_count");
2092
        goto out;
2093
    }
2094
    ret = dict_get_str(peer_data, "peername", &peername);
2095
    if (ret) {
2096
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2097
               "Failed to fetch peername");
2098
        goto out;
2099
    }
2100

2101
    for (i = 1; i <= snap_count; i++) {
2102
        snprintf(prefix, sizeof(prefix), "snap%d", i);
2103

2104
        /* Fetch the peer's snapname */
2105
        snprintf(buf, sizeof(buf), "%s.snapname", prefix);
2106
        ret = dict_get_str(peer_data, buf, &peer_snap_name);
2107
        if (ret) {
2108
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2109
                   "Unable to fetch snapname from peer: %s", peername);
2110
            goto out;
2111
        }
2112

2113
        /* Fetch the peer's snap_id */
2114
        snprintf(buf, sizeof(buf), "%s.snap_id", prefix);
2115
        ret = dict_get_str(peer_data, buf, &peer_snap_id);
2116
        if (ret) {
2117
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2118
                   "Unable to fetch snap_id from peer: %s", peername);
2119
            goto out;
2120
        }
2121

2122
        /* remove_my_data */
2123
        snprintf(buf, sizeof(buf), "%s.remove_my_data", prefix);
2124
        ret = dict_get_int32(peer_data, buf, &val);
2125
        if (val)
2126
            remove_my_data = _gf_true;
2127
        else
2128
            remove_my_data = _gf_false;
2129

2130
        if (remove_my_data) {
2131
            snprintf(buf, sizeof(buf), "%s.remove_lvm", prefix);
2132
            ret = dict_get_int32(peer_data, buf, &val);
2133
            if (val)
2134
                remove_lvm = _gf_true;
2135
            else
2136
                remove_lvm = _gf_false;
2137

2138
            dict = dict_new();
2139
            if (!dict) {
2140
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_CREATE_FAIL,
2141
                       "Unable to create dict");
2142
                ret = -1;
2143
                goto out;
2144
            }
2145
            snap = glusterd_find_snap_by_name(peer_snap_name);
2146
            if (!snap) {
2147
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_MISSED_SNAP_PRESENT,
2148
                       "Snapshot %s from peer %s missing on "
2149
                       "localhost",
2150
                       peer_snap_name, peername);
2151
                ret = -1;
2152
                goto out;
2153
            }
2154

2155
            ret = glusterd_snap_remove(dict, snap, remove_lvm, _gf_false,
2156
                                       _gf_false);
2157
            if (ret) {
2158
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_REMOVE_FAIL,
2159
                       "Failed to remove snap %s", snap->snapname);
2160
                goto out;
2161
            }
2162

2163
            dict_unref(dict);
2164
            dict = NULL;
2165
        }
2166
        snprintf(buf, sizeof(buf), "%s.accept_peer_data", prefix);
2167
        ret = dict_get_int32(peer_data, buf, &val);
2168
        if (val)
2169
            accept_peer_data = _gf_true;
2170
        else
2171
            accept_peer_data = _gf_false;
2172

2173
        if (accept_peer_data) {
2174
            /* Accept Peer Data */
2175
            ret = glusterd_import_friend_snap(peer_data, i, peer_snap_name,
2176
                                              peer_snap_id);
2177
            if (ret) {
2178
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_IMPORT_FAIL,
2179
                       "Failed to import snap %s from peer %s", peer_snap_name,
2180
                       peername);
2181
                goto out;
2182
            }
2183
        }
2184
    }
2185

2186
out:
2187
    if (peer_data)
2188
        dict_unref(peer_data);
2189
    if (dict)
2190
        dict_unref(dict);
2191
    conf->restart_bricks = _gf_false;
2192
    synccond_broadcast(&conf->cond_restart_bricks);
2193

2194
    return ret;
2195
}
2196

2197
/* Compare snapshots present in peer_data, with the snapshots in
2198
 * the current node
2199
 */
2200
int32_t
2201
glusterd_compare_friend_snapshots(dict_t *peer_data, char *peername,
2202
                                  uuid_t peerid)
2203
{
2204
    int32_t ret = -1;
2205
    int32_t snap_count = 0;
2206
    int i = 1;
2207
    xlator_t *this = THIS;
2208
    dict_t *peer_data_copy = NULL;
2209

2210
    GF_ASSERT(peer_data);
2211
    GF_ASSERT(peername);
2212

2213
    ret = dict_get_int32(peer_data, "snap_count", &snap_count);
2214
    if (ret) {
2215
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2216
               "Failed to fetch snap_count");
2217
        goto out;
2218
    }
2219

2220
    if (!snap_count)
2221
        goto out;
2222

2223
    for (i = 1; i <= snap_count; i++) {
2224
        /* Compare one snapshot from peer_data at a time */
2225
        ret = glusterd_compare_snap(peer_data, i, peername, peerid);
2226
        if (ret) {
2227
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAPSHOT_OP_FAILED,
2228
                   "Failed to compare snapshots with peer %s", peername);
2229
            goto out;
2230
        }
2231
    }
2232
    /* Update the snaps at one go */
2233
    peer_data_copy = dict_copy_with_ref(peer_data, NULL);
2234
    ret = dict_set_str(peer_data_copy, "peername", peername);
2235
    if (ret) {
2236
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2237
               "Failed to set peername into the dict");
2238
        if (peer_data_copy)
2239
            dict_unref(peer_data_copy);
2240
        goto out;
2241
    }
2242
    glusterd_launch_synctask(glusterd_update_snaps_synctask, peer_data_copy);
2243

2244
out:
2245
    gf_msg_trace(this->name, 0, "Returning %d", ret);
2246
    return ret;
2247
}
2248

2249
int32_t
2250
glusterd_add_snapd_to_dict(glusterd_volinfo_t *volinfo, dict_t *dict,
2251
                           int32_t count)
2252
{
2253
    int ret = -1;
2254
    int32_t pid = -1;
2255
    int32_t brick_online = -1;
2256
    char key[64] = {0};
2257
    char base_key[32] = {0};
2258
    char pidfile[PATH_MAX] = {0};
2259
    xlator_t *this = THIS;
2260

2261
    GF_ASSERT(volinfo);
2262
    GF_ASSERT(dict);
2263

2264
    snprintf(base_key, sizeof(base_key), "brick%d", count);
2265
    snprintf(key, sizeof(key), "%s.hostname", base_key);
2266
    ret = dict_set_str(dict, key, "Snapshot Daemon");
2267
    if (ret) {
2268
        gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s",
2269
                key, NULL);
2270
        goto out;
2271
    }
2272

2273
    snprintf(key, sizeof(key), "%s.path", base_key);
2274
    ret = dict_set_dynstr(dict, key, gf_strdup(uuid_utoa(MY_UUID)));
2275
    if (ret) {
2276
        gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s",
2277
                key, NULL);
2278
        goto out;
2279
    }
2280

2281
    snprintf(key, sizeof(key), "%s.port", base_key);
2282
    ret = dict_set_int32(dict, key, volinfo->snapd.port);
2283
    if (ret) {
2284
        gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s",
2285
                key, NULL);
2286
        goto out;
2287
    }
2288

2289
    glusterd_svc_build_snapd_pidfile(volinfo, pidfile, sizeof(pidfile));
2290

2291
    brick_online = gf_is_service_running(pidfile, &pid);
2292
    if (brick_online == _gf_false)
2293
        pid = -1;
2294

2295
    snprintf(key, sizeof(key), "%s.pid", base_key);
2296
    ret = dict_set_int32(dict, key, pid);
2297
    if (ret) {
2298
        gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED, "Key=%s",
2299
                key, NULL);
2300
        goto out;
2301
    }
2302

2303
    snprintf(key, sizeof(key), "%s.status", base_key);
2304
    ret = dict_set_int32(dict, key, brick_online);
2305

2306
out:
2307
    if (ret)
2308
        gf_msg_debug(this->name, 0, "Returning %d", ret);
2309

2310
    return ret;
2311
}
2312

2313
int
2314
glusterd_snap_config_use_rsp_dict(dict_t *dst, dict_t *src)
2315
{
2316
    char buf[PATH_MAX] = "";
2317
    char *volname = NULL;
2318
    int ret = -1;
2319
    int config_command = 0;
2320
    uint64_t i = 0;
2321
    uint64_t hard_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT;
2322
    uint64_t soft_limit = GLUSTERD_SNAPS_DEF_SOFT_LIMIT_PERCENT;
2323
    uint64_t value = 0;
2324
    uint64_t voldisplaycount = 0;
2325

2326
    if (!dst || !src) {
2327
        gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_EMPTY,
2328
               "Source or Destination "
2329
               "dict is empty.");
2330
        goto out;
2331
    }
2332

2333
    ret = dict_get_int32(dst, "config-command", &config_command);
2334
    if (ret) {
2335
        gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2336
               "failed to get config-command type");
2337
        goto out;
2338
    }
2339

2340
    switch (config_command) {
2341
        case GF_SNAP_CONFIG_DISPLAY:
2342
            ret = dict_get_uint64(src, GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT,
2343
                                  &hard_limit);
2344
            if (!ret) {
2345
                ret = dict_set_uint64(
2346
                    dst, GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT, hard_limit);
2347
                if (ret) {
2348
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2349
                           "Unable to set snap_max_hard_limit");
2350
                    goto out;
2351
                }
2352
            } else {
2353
                /* Received dummy response from other nodes */
2354
                ret = 0;
2355
                goto out;
2356
            }
2357

2358
            ret = dict_get_uint64(src, GLUSTERD_STORE_KEY_SNAP_MAX_SOFT_LIMIT,
2359
                                  &soft_limit);
2360
            if (ret) {
2361
                gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2362
                       "Unable to get snap_max_soft_limit");
2363
                goto out;
2364
            }
2365

2366
            ret = dict_set_uint64(dst, GLUSTERD_STORE_KEY_SNAP_MAX_SOFT_LIMIT,
2367
                                  soft_limit);
2368
            if (ret) {
2369
                gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2370
                       "Unable to set snap_max_soft_limit");
2371
                goto out;
2372
            }
2373

2374
            ret = dict_get_uint64(src, "voldisplaycount", &voldisplaycount);
2375
            if (ret) {
2376
                gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2377
                       "Unable to get voldisplaycount");
2378
                goto out;
2379
            }
2380

2381
            ret = dict_set_uint64(dst, "voldisplaycount", voldisplaycount);
2382
            if (ret) {
2383
                gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2384
                       "Unable to set voldisplaycount");
2385
                goto out;
2386
            }
2387

2388
            for (i = 0; i < voldisplaycount; i++) {
2389
                snprintf(buf, sizeof(buf), "volume%" PRIu64 "-volname", i);
2390
                ret = dict_get_str(src, buf, &volname);
2391
                if (ret) {
2392
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2393
                           "Unable to get %s", buf);
2394
                    goto out;
2395
                }
2396
                ret = dict_set_str(dst, buf, volname);
2397
                if (ret) {
2398
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2399
                           "Unable to set %s", buf);
2400
                    goto out;
2401
                }
2402

2403
                snprintf(buf, sizeof(buf),
2404
                         "volume%" PRIu64 "-snap-max-hard-limit", i);
2405
                ret = dict_get_uint64(src, buf, &value);
2406
                if (ret) {
2407
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2408
                           "Unable to get %s", buf);
2409
                    goto out;
2410
                }
2411
                ret = dict_set_uint64(dst, buf, value);
2412
                if (ret) {
2413
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2414
                           "Unable to set %s", buf);
2415
                    goto out;
2416
                }
2417

2418
                snprintf(buf, sizeof(buf),
2419
                         "volume%" PRIu64 "-active-hard-limit", i);
2420
                ret = dict_get_uint64(src, buf, &value);
2421
                if (ret) {
2422
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2423
                           "Unable to get %s", buf);
2424
                    goto out;
2425
                }
2426
                ret = dict_set_uint64(dst, buf, value);
2427
                if (ret) {
2428
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2429
                           "Unable to set %s", buf);
2430
                    goto out;
2431
                }
2432

2433
                snprintf(buf, sizeof(buf),
2434
                         "volume%" PRIu64 "-snap-max-soft-limit", i);
2435
                ret = dict_get_uint64(src, buf, &value);
2436
                if (ret) {
2437
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2438
                           "Unable to get %s", buf);
2439
                    goto out;
2440
                }
2441
                ret = dict_set_uint64(dst, buf, value);
2442
                if (ret) {
2443
                    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2444
                           "Unable to set %s", buf);
2445
                    goto out;
2446
                }
2447
            }
2448

2449
            break;
2450
        default:
2451
            break;
2452
    }
2453

2454
    ret = 0;
2455
out:
2456
    gf_msg_debug("glusterd", 0, "Returning %d", ret);
2457
    return ret;
2458
}
2459

2460
int
2461
glusterd_merge_brick_status(dict_t *dst, dict_t *src)
2462
{
2463
    int64_t volume_count = 0;
2464
    int64_t index = 0;
2465
    int64_t j = 0;
2466
    int64_t brick_count = 0;
2467
    int64_t brick_order = 0;
2468
    char key[64] = {
2469
        0,
2470
    };
2471
    char key_prefix[16] = {
2472
        0,
2473
    };
2474
    char snapbrckcnt[PATH_MAX] = {
2475
        0,
2476
    };
2477
    char snapbrckord[PATH_MAX] = {
2478
        0,
2479
    };
2480
    char *clonename = NULL;
2481
    int ret = -1;
2482
    int32_t brick_online = 0;
2483
    xlator_t *this = THIS;
2484
    int32_t snap_command = 0;
2485

2486
    if (!dst || !src) {
2487
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_EMPTY,
2488
               "Source or Destination "
2489
               "dict is empty.");
2490
        goto out;
2491
    }
2492

2493
    ret = dict_get_int32(dst, "type", &snap_command);
2494
    if (ret) {
2495
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2496
               "unable to get the type of "
2497
               "the snapshot command");
2498
        goto out;
2499
    }
2500

2501
    if (snap_command == GF_SNAP_OPTION_TYPE_DELETE) {
2502
        gf_msg_debug(this->name, 0,
2503
                     "snapshot delete command."
2504
                     " Need not merge the status of the bricks");
2505
        ret = 0;
2506
        goto out;
2507
    }
2508

2509
    /* Try and fetch clonename. If present set status with clonename *
2510
     * else do so as snap-vol */
2511
    ret = dict_get_str(dst, "clonename", &clonename);
2512
    if (ret) {
2513
        snprintf(key_prefix, sizeof(key_prefix), "snap-vol");
2514
    } else
2515
        snprintf(key_prefix, sizeof(key_prefix), "clone");
2516

2517
    ret = dict_get_int64(src, "volcount", &volume_count);
2518
    if (ret) {
2519
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2520
               "failed to "
2521
               "get the volume count");
2522
        goto out;
2523
    }
2524

2525
    for (index = 0; index < volume_count; index++) {
2526
        ret = snprintf(snapbrckcnt, sizeof(snapbrckcnt) - 1,
2527
                       "snap-vol%" PRId64 "_brickcount", index + 1);
2528
        ret = dict_get_int64(src, snapbrckcnt, &brick_count);
2529
        if (ret) {
2530
            gf_msg_trace(this->name, 0,
2531
                         "No bricks for this volume in this dict (%s)",
2532
                         snapbrckcnt);
2533
            continue;
2534
        }
2535

2536
        for (j = 0; j < brick_count; j++) {
2537
            /* Fetching data from source dict */
2538
            snprintf(snapbrckord, sizeof(snapbrckord) - 1,
2539
                     "snap-vol%" PRId64 ".brick%" PRId64 ".order", index + 1,
2540
                     j);
2541

2542
            ret = dict_get_int64(src, snapbrckord, &brick_order);
2543
            if (ret) {
2544
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2545
                       "Failed to get brick order (%s)", snapbrckord);
2546
                goto out;
2547
            }
2548

2549
            snprintf(key, sizeof(key), "%s%" PRId64 ".brick%" PRId64 ".status",
2550
                     key_prefix, index + 1, brick_order);
2551
            ret = dict_get_int32(src, key, &brick_online);
2552
            if (ret) {
2553
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2554
                       "failed to "
2555
                       "get the brick status (%s)",
2556
                       key);
2557
                goto out;
2558
            }
2559

2560
            ret = dict_set_int32(dst, key, brick_online);
2561
            if (ret) {
2562
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2563
                       "failed to "
2564
                       "set the brick status (%s)",
2565
                       key);
2566
                goto out;
2567
            }
2568
            brick_online = 0;
2569
        }
2570
    }
2571

2572
    ret = 0;
2573

2574
out:
2575
    return ret;
2576
}
2577

2578
/* Aggregate missed_snap_counts from different nodes and save it *
2579
 * in the req_dict of the originator node */
2580
int
2581
glusterd_snap_create_use_rsp_dict(dict_t *dst, dict_t *src)
2582
{
2583
    char *buf = NULL;
2584
    char *tmp_str = NULL;
2585
    char name_buf[PATH_MAX] = "";
2586
    int32_t i = -1;
2587
    int32_t ret = -1;
2588
    int32_t src_missed_snap_count = -1;
2589
    int32_t dst_missed_snap_count = -1;
2590
    xlator_t *this = THIS;
2591
    int8_t soft_limit_flag = -1;
2592

2593
    if (!dst || !src) {
2594
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_EMPTY,
2595
               "Source or Destination "
2596
               "dict is empty.");
2597
        goto out;
2598
    }
2599

2600
    ret = glusterd_merge_brick_status(dst, src);
2601
    if (ret) {
2602
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_BRICK_SET_INFO_FAIL,
2603
               "failed to merge brick "
2604
               "status");
2605
        goto out;
2606
    }
2607

2608
    ret = dict_get_str(src, "snapuuid", &buf);
2609
    if (ret) {
2610
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2611
               "failed to get snap UUID");
2612
        goto out;
2613
    }
2614

2615
    ret = dict_set_dynstr_with_alloc(dst, "snapuuid", buf);
2616
    if (ret) {
2617
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2618
               "Failed to set snap uuid in dict");
2619
        goto out;
2620
    }
2621

2622
    /* set in dst dictionary soft-limit-reach only if soft-limit-reach
2623
     * is present src dictionary */
2624
    ret = dict_get_int8(src, "soft-limit-reach", &soft_limit_flag);
2625
    if (!ret) {
2626
        ret = dict_set_int8(dst, "soft-limit-reach", soft_limit_flag);
2627
        if (ret) {
2628
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2629
                   "Failed to set "
2630
                   "soft_limit_flag");
2631
            goto out;
2632
        }
2633
    }
2634

2635
    ret = dict_get_int32(src, "missed_snap_count", &src_missed_snap_count);
2636
    if (ret) {
2637
        gf_msg_debug(this->name, 0, "No missed snaps");
2638
        ret = 0;
2639
        goto out;
2640
    }
2641

2642
    ret = dict_get_int32(dst, "missed_snap_count", &dst_missed_snap_count);
2643
    if (ret) {
2644
        /* Initialize dst_missed_count for the first time */
2645
        dst_missed_snap_count = 0;
2646
    }
2647

2648
    for (i = 0; i < src_missed_snap_count; i++) {
2649
        snprintf(name_buf, sizeof(name_buf), "missed_snaps_%d", i);
2650
        ret = dict_get_str(src, name_buf, &buf);
2651
        if (ret) {
2652
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2653
                   "Unable to fetch %s", name_buf);
2654
            goto out;
2655
        }
2656

2657
        snprintf(name_buf, sizeof(name_buf), "missed_snaps_%d",
2658
                 dst_missed_snap_count);
2659

2660
        tmp_str = gf_strdup(buf);
2661
        if (!tmp_str) {
2662
            ret = -1;
2663
            goto out;
2664
        }
2665

2666
        ret = dict_set_dynstr(dst, name_buf, tmp_str);
2667
        if (ret) {
2668
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2669
                   "Unable to set %s", name_buf);
2670
            goto out;
2671
        }
2672

2673
        tmp_str = NULL;
2674
        dst_missed_snap_count++;
2675
    }
2676

2677
    ret = dict_set_int32(dst, "missed_snap_count", dst_missed_snap_count);
2678
    if (ret) {
2679
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
2680
               "Unable to set dst_missed_snap_count");
2681
        goto out;
2682
    }
2683

2684
out:
2685
    if (ret && tmp_str)
2686
        GF_FREE(tmp_str);
2687

2688
    gf_msg_trace(this->name, 0, "Returning %d", ret);
2689
    return ret;
2690
}
2691

2692
int
2693
glusterd_snap_use_rsp_dict(dict_t *dst, dict_t *src)
2694
{
2695
    int ret = -1;
2696
    int32_t snap_command = 0;
2697

2698
    if (!dst || !src) {
2699
        gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_EMPTY,
2700
               "Source or Destination "
2701
               "dict is empty.");
2702
        goto out;
2703
    }
2704

2705
    ret = dict_get_int32(dst, "type", &snap_command);
2706
    if (ret) {
2707
        gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
2708
               "unable to get the type of "
2709
               "the snapshot command");
2710
        goto out;
2711
    }
2712

2713
    switch (snap_command) {
2714
        case GF_SNAP_OPTION_TYPE_CREATE:
2715
        case GF_SNAP_OPTION_TYPE_DELETE:
2716
        case GF_SNAP_OPTION_TYPE_CLONE:
2717
            ret = glusterd_snap_create_use_rsp_dict(dst, src);
2718
            if (ret) {
2719
                gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_RSP_DICT_USE_FAIL,
2720
                       "Unable to use rsp dict");
2721
                goto out;
2722
            }
2723
            break;
2724
        case GF_SNAP_OPTION_TYPE_CONFIG:
2725
            ret = glusterd_snap_config_use_rsp_dict(dst, src);
2726
            if (ret) {
2727
                gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_RSP_DICT_USE_FAIL,
2728
                       "Unable to use rsp dict");
2729
                goto out;
2730
            }
2731
            break;
2732
        default:
2733
            /* copy the response dictinary's contents to the dict to be
2734
             * sent back to the cli */
2735
            dict_copy(src, dst);
2736
            break;
2737
    }
2738

2739
    ret = 0;
2740
out:
2741
    gf_msg_debug("glusterd", 0, "Returning %d", ret);
2742
    return ret;
2743
}
2744

2745
int
2746
glusterd_compare_snap_time(struct cds_list_head *list1,
2747
                           struct cds_list_head *list2)
2748
{
2749
    glusterd_snap_t *snap1 = NULL;
2750
    glusterd_snap_t *snap2 = NULL;
2751
    double diff_time = 0;
2752

2753
    GF_ASSERT(list1);
2754
    GF_ASSERT(list2);
2755

2756
    snap1 = cds_list_entry(list1, glusterd_snap_t, snap_list);
2757
    snap2 = cds_list_entry(list2, glusterd_snap_t, snap_list);
2758
    diff_time = difftime(snap1->time_stamp, snap2->time_stamp);
2759

2760
    return (int)diff_time;
2761
}
2762

2763
int
2764
glusterd_compare_snap_vol_time(struct cds_list_head *list1,
2765
                               struct cds_list_head *list2)
2766
{
2767
    glusterd_volinfo_t *snapvol1 = NULL;
2768
    glusterd_volinfo_t *snapvol2 = NULL;
2769
    double diff_time = 0;
2770

2771
    GF_ASSERT(list1);
2772
    GF_ASSERT(list2);
2773

2774
    snapvol1 = cds_list_entry(list1, glusterd_volinfo_t, snapvol_list);
2775
    snapvol2 = cds_list_entry(list2, glusterd_volinfo_t, snapvol_list);
2776
    diff_time = difftime(snapvol1->snapshot->time_stamp,
2777
                         snapvol2->snapshot->time_stamp);
2778

2779
    return (int)diff_time;
2780
}
2781

2782
int32_t
2783
glusterd_missed_snapinfo_new(glusterd_missed_snap_info **missed_snapinfo)
2784
{
2785
    glusterd_missed_snap_info *new_missed_snapinfo = NULL;
2786
    int32_t ret = -1;
2787
    xlator_t *this = THIS;
2788

2789
    GF_ASSERT(missed_snapinfo);
2790

2791
    new_missed_snapinfo = GF_CALLOC(1, sizeof(*new_missed_snapinfo),
2792
                                    gf_gld_mt_missed_snapinfo_t);
2793

2794
    if (!new_missed_snapinfo) {
2795
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
2796
        goto out;
2797
    }
2798

2799
    CDS_INIT_LIST_HEAD(&new_missed_snapinfo->missed_snaps);
2800
    CDS_INIT_LIST_HEAD(&new_missed_snapinfo->snap_ops);
2801

2802
    *missed_snapinfo = new_missed_snapinfo;
2803

2804
    ret = 0;
2805

2806
out:
2807
    gf_msg_trace(this->name, 0, "Returning %d", ret);
2808
    return ret;
2809
}
2810

2811
int32_t
2812
glusterd_missed_snap_op_new(glusterd_snap_op_t **snap_op)
2813
{
2814
    glusterd_snap_op_t *new_snap_op = NULL;
2815
    int32_t ret = -1;
2816
    xlator_t *this = THIS;
2817

2818
    GF_ASSERT(snap_op);
2819

2820
    new_snap_op = GF_CALLOC(1, sizeof(*new_snap_op),
2821
                            gf_gld_mt_missed_snapinfo_t);
2822

2823
    if (!new_snap_op) {
2824
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
2825
        goto out;
2826
    }
2827

2828
    new_snap_op->brick_num = -1;
2829
    new_snap_op->op = -1;
2830
    new_snap_op->status = -1;
2831
    CDS_INIT_LIST_HEAD(&new_snap_op->snap_ops_list);
2832

2833
    *snap_op = new_snap_op;
2834

2835
    ret = 0;
2836
out:
2837
    gf_msg_trace(this->name, 0, "Returning %d", ret);
2838
    return ret;
2839
}
2840

2841
gf_boolean_t
2842
glusterd_mntopts_exists(const char *str, const char *opts)
2843
{
2844
    char *dup_val = NULL;
2845
    char *savetok = NULL;
2846
    char *token = NULL;
2847
    gf_boolean_t exists = _gf_false;
2848

2849
    GF_ASSERT(opts);
2850

2851
    if (!str || !strlen(str))
2852
        goto out;
2853

2854
    dup_val = gf_strdup(str);
2855
    if (!dup_val)
2856
        goto out;
2857

2858
    token = strtok_r(dup_val, ",", &savetok);
2859
    while (token) {
2860
        if (!strcmp(token, opts)) {
2861
            exists = _gf_true;
2862
            goto out;
2863
        }
2864
        token = strtok_r(NULL, ",", &savetok);
2865
    }
2866

2867
out:
2868
    GF_FREE(dup_val);
2869
    return exists;
2870
}
2871

2872
static int32_t
2873
glusterd_volume_quorum_check(glusterd_volinfo_t *volinfo, int64_t index,
2874
                             dict_t *dict, const char *key_prefix,
2875
                             char **op_errstr, uint32_t *op_errno)
2876
{
2877
    int ret = 0;
2878
    xlator_t *this = THIS;
2879
    int64_t i = 0;
2880
    int64_t j = 0;
2881
    char key[128] = {
2882
        0,
2883
    }; /* key_prefix is passed from above, but is really quite small */
2884
    int keylen;
2885
    glusterd_conf_t *priv = NULL;
2886
    gf_boolean_t quorum_met = _gf_false;
2887
    int distribute_subvols = 0;
2888
    int32_t brick_online = 0;
2889
    const char err_str[] = "One or more bricks may be down.";
2890

2891
    priv = this->private;
2892
    GF_ASSERT(priv);
2893
    GF_VALIDATE_OR_GOTO(this->name, op_errno, out);
2894

2895
    if (!volinfo || !dict) {
2896
        gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_INVALID_ENTRY,
2897
               "input parameters NULL");
2898
        goto out;
2899
    }
2900

2901
    if ((!glusterd_is_volume_replicate(volinfo) ||
2902
         volinfo->replica_count < 3) &&
2903
        (GF_CLUSTER_TYPE_DISPERSE != volinfo->type)) {
2904
        for (i = 0; i < volinfo->brick_count; i++) {
2905
            /* for a pure distribute volume, and replica volume
2906
               with replica count 2, quorum is not met if even
2907
               one of its subvolumes is down
2908
            */
2909
            keylen = snprintf(key, sizeof(key),
2910
                              "%s%" PRId64 ".brick%" PRId64 ".status",
2911
                              key_prefix, index, i);
2912
            ret = dict_get_int32n(dict, key, keylen, &brick_online);
2913
            if (ret || !brick_online) {
2914
                ret = 1;
2915
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_BRICK_DISCONNECTED,
2916
                       "%s", err_str);
2917
                *op_errstr = gf_strdup(err_str);
2918
                *op_errno = EG_BRCKDWN;
2919
                goto out;
2920
            }
2921
        }
2922
    } else {
2923
        distribute_subvols = volinfo->brick_count / volinfo->dist_leaf_count;
2924
        for (j = 0; j < distribute_subvols; j++) {
2925
            /* by default assume quorum is not met
2926
               Currently only distributed replicate volumes are
2927
               handled. quorum is not met even if one of the bricks are down.
2928
            */
2929
            ret = 1;
2930
            for (i = 0; i < volinfo->dist_leaf_count; i++) {
2931
                keylen = snprintf(
2932
                    key, sizeof(key), "%s%" PRId64 ".brick%" PRId64 ".status",
2933
                    key_prefix, index, (j * volinfo->dist_leaf_count) + i);
2934
                ret = dict_get_int32n(dict, key, keylen, &brick_online);
2935
                if (ret || !brick_online) {
2936
                    ret = -1;
2937
                    gf_msg(this->name, GF_LOG_ERROR, 0,
2938
                           GD_MSG_BRICK_DISCONNECTED, "%s", err_str);
2939
                    *op_errstr = gf_strdup(err_str);
2940
                    *op_errno = EG_BRCKDWN;
2941
                    goto out;
2942
                }
2943
            }
2944
        }
2945
    }
2946

2947
    quorum_met = _gf_true;
2948
    if (quorum_met) {
2949
        gf_msg_debug(this->name, 0, "All bricks in volume %s are online.",
2950
                     volinfo->volname);
2951
        ret = 0;
2952
    }
2953

2954
out:
2955
    return ret;
2956
}
2957

2958
static int32_t
2959
glusterd_snap_common_quorum_calculate(glusterd_volinfo_t *volinfo, dict_t *dict,
2960
                                      int64_t index, const char *key_prefix,
2961
                                      char **op_errstr, uint32_t *op_errno)
2962
{
2963
    int32_t ret = -1;
2964
    xlator_t *this = THIS;
2965

2966
    GF_VALIDATE_OR_GOTO(this->name, op_errno, out);
2967
    GF_VALIDATE_OR_GOTO(this->name, volinfo, out);
2968

2969
    ret = glusterd_volume_quorum_check(volinfo, index, dict, key_prefix,
2970
                                       op_errstr, op_errno);
2971
    if (ret) {
2972
        gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_NOT_FOUND,
2973
               "volume %s "
2974
               "is not in quorum",
2975
               volinfo->volname);
2976
        goto out;
2977
    }
2978

2979
out:
2980
    return ret;
2981
}
2982

2983
static int32_t
2984
glusterd_snap_quorum_check_for_clone(dict_t *dict, gf_boolean_t snap_volume,
2985
                                     char **op_errstr, uint32_t *op_errno)
2986
{
2987
    const char err_str[] = "glusterds are not in quorum";
2988
    char key_prefix[16] = {
2989
        0,
2990
    };
2991
    char *snapname = NULL;
2992
    glusterd_snap_t *snap = NULL;
2993
    glusterd_volinfo_t *volinfo = NULL;
2994
    glusterd_volinfo_t *tmp_volinfo = NULL;
2995
    char *volname = NULL;
2996
    int64_t volcount = 0;
2997
    int64_t i = 0;
2998
    int32_t ret = -1;
2999
    xlator_t *this = THIS;
3000

3001
    GF_VALIDATE_OR_GOTO(this->name, op_errno, out);
3002

3003
    if (!dict) {
3004
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_EMPTY, "dict is NULL");
3005
        goto out;
3006
    }
3007

3008
    if (snap_volume) {
3009
        ret = dict_get_str(dict, "snapname", &snapname);
3010
        if (ret) {
3011
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3012
                   "failed to "
3013
                   "get snapname");
3014
            goto out;
3015
        }
3016

3017
        snap = glusterd_find_snap_by_name(snapname);
3018
        if (!snap) {
3019
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_NOT_FOUND,
3020
                   "failed to "
3021
                   "get the snapshot %s",
3022
                   snapname);
3023
            ret = -1;
3024
            goto out;
3025
        }
3026
    }
3027

3028
    /* Do a quorum check of glusterds also. Because, the missed snapshot
3029
     * information will be saved by glusterd and if glusterds are not in
3030
     * quorum, then better fail the snapshot
3031
     */
3032
    if (!does_gd_meet_server_quorum(this)) {
3033
        gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_SERVER_QUORUM_NOT_MET,
3034
               "%s", err_str);
3035
        *op_errstr = gf_strdup(err_str);
3036
        *op_errno = EG_NODEDWN;
3037
        ret = -1;
3038
        goto out;
3039
    } else
3040
        gf_msg_debug(this->name, 0, "glusterds are in quorum");
3041

3042
    ret = dict_get_int64(dict, "volcount", &volcount);
3043
    if (ret) {
3044
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3045
               "failed to get "
3046
               "volcount");
3047
        goto out;
3048
    }
3049

3050
    for (i = 1; i <= volcount; i++) {
3051
        ret = dict_get_str(dict, "clonename", &volname);
3052
        if (ret) {
3053
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3054
                   "failed to "
3055
                   "get clonename");
3056
            goto out;
3057
        }
3058

3059
        if (snap_volume && snap) {
3060
            cds_list_for_each_entry(tmp_volinfo, &snap->volumes, vol_list)
3061
            {
3062
                if (!tmp_volinfo) {
3063
                    gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_NOT_FOUND,
3064
                           "failed to get snap volume "
3065
                           "for snap %s",
3066
                           snapname);
3067
                    ret = -1;
3068
                    goto out;
3069
                }
3070
                volinfo = tmp_volinfo;
3071
            }
3072
        } else {
3073
            ret = glusterd_volinfo_find(volname, &volinfo);
3074
            if (ret) {
3075
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOL_NOT_FOUND,
3076
                       "failed to find the volume %s", volname);
3077
                goto out;
3078
            }
3079
        }
3080

3081
        snprintf(key_prefix, sizeof(key_prefix), "%s",
3082
                 snap_volume ? "vol" : "clone");
3083

3084
        ret = glusterd_snap_common_quorum_calculate(
3085
            volinfo, dict, i, key_prefix, op_errstr, op_errno);
3086
        if (ret) {
3087
            gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_NOT_FOUND,
3088
                   "volume %s "
3089
                   "is not in quorum",
3090
                   volname);
3091
            goto out;
3092
        }
3093
    }
3094
out:
3095
    return ret;
3096
}
3097

3098
static int32_t
3099
glusterd_snap_quorum_check_for_create(dict_t *dict, gf_boolean_t snap_volume,
3100
                                      char **op_errstr, uint32_t *op_errno)
3101
{
3102
    const char err_str[] = "glusterds are not in quorum";
3103
    char key_prefix[16] = {
3104
        0,
3105
    };
3106
    char *snapname = NULL;
3107
    glusterd_snap_t *snap = NULL;
3108
    glusterd_volinfo_t *volinfo = NULL;
3109
    char *volname = NULL;
3110
    int64_t volcount = 0;
3111
    char key[32] = {
3112
        0,
3113
    };
3114
    int64_t i = 0;
3115
    int32_t ret = -1;
3116
    xlator_t *this = THIS;
3117

3118
    GF_VALIDATE_OR_GOTO(this->name, op_errno, out);
3119

3120
    if (!dict) {
3121
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_EMPTY, "dict is NULL");
3122
        goto out;
3123
    }
3124

3125
    if (snap_volume) {
3126
        ret = dict_get_str(dict, "snapname", &snapname);
3127
        if (ret) {
3128
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3129
                   "failed to "
3130
                   "get snapname");
3131
            goto out;
3132
        }
3133

3134
        snap = glusterd_find_snap_by_name(snapname);
3135
        if (!snap) {
3136
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_NOT_FOUND,
3137
                   "failed to "
3138
                   "get the snapshot %s",
3139
                   snapname);
3140
            ret = -1;
3141
            goto out;
3142
        }
3143
    }
3144

3145
    /* Do a quorum check of glusterds also. Because, the missed snapshot
3146
     * information will be saved by glusterd and if glusterds are not in
3147
     * quorum, then better fail the snapshot
3148
     */
3149
    if (!does_gd_meet_server_quorum(this)) {
3150
        gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_SERVER_QUORUM_NOT_MET,
3151
               "%s", err_str);
3152
        *op_errstr = gf_strdup(err_str);
3153
        *op_errno = EG_NODEDWN;
3154
        ret = -1;
3155
        goto out;
3156
    } else
3157
        gf_msg_debug(this->name, 0, "glusterds are in quorum");
3158

3159
    ret = dict_get_int64(dict, "volcount", &volcount);
3160
    if (ret) {
3161
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3162
               "failed to get "
3163
               "volcount");
3164
        goto out;
3165
    }
3166

3167
    for (i = 1; i <= volcount; i++) {
3168
        snprintf(key, sizeof(key), "%s%" PRId64,
3169
                 snap_volume ? "snap-volname" : "volname", i);
3170
        ret = dict_get_str(dict, key, &volname);
3171
        if (ret) {
3172
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3173
                   "failed to "
3174
                   "get volname");
3175
            goto out;
3176
        }
3177

3178
        if (snap_volume) {
3179
            ret = glusterd_snap_volinfo_find(volname, snap, &volinfo);
3180
            if (ret) {
3181
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SNAP_NOT_FOUND,
3182
                       "failed to get snap volume %s "
3183
                       "for snap %s",
3184
                       volname, snapname);
3185
                goto out;
3186
            }
3187
        } else {
3188
            ret = glusterd_volinfo_find(volname, &volinfo);
3189
            if (ret) {
3190
                gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOL_NOT_FOUND,
3191
                       "failed to find the volume %s", volname);
3192
                goto out;
3193
            }
3194
        }
3195

3196
        snprintf(key_prefix, sizeof(key_prefix), "%s",
3197
                 snap_volume ? "snap-vol" : "vol");
3198

3199
        ret = glusterd_snap_common_quorum_calculate(
3200
            volinfo, dict, i, key_prefix, op_errstr, op_errno);
3201
        if (ret) {
3202
            gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_VOL_NOT_FOUND,
3203
                   "volume %s "
3204
                   "is not in quorum",
3205
                   volinfo->volname);
3206
            goto out;
3207
        }
3208
    }
3209
out:
3210
    return ret;
3211
}
3212

3213
int32_t
3214
glusterd_snap_quorum_check(dict_t *dict, gf_boolean_t snap_volume,
3215
                           char **op_errstr, uint32_t *op_errno)
3216
{
3217
    int32_t ret = -1;
3218
    xlator_t *this = THIS;
3219
    int32_t snap_command = 0;
3220
    const char err_str[] = "glusterds are not in quorum";
3221

3222
    GF_VALIDATE_OR_GOTO(this->name, op_errno, out);
3223

3224
    if (!dict) {
3225
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_EMPTY, "dict is NULL");
3226
        goto out;
3227
    }
3228

3229
    ret = dict_get_int32(dict, "type", &snap_command);
3230
    if (ret) {
3231
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3232
               "unable to get the type of "
3233
               "the snapshot command");
3234
        goto out;
3235
    }
3236

3237
    switch (snap_command) {
3238
        case GF_SNAP_OPTION_TYPE_CREATE:
3239
            ret = glusterd_snap_quorum_check_for_create(dict, snap_volume,
3240
                                                        op_errstr, op_errno);
3241
            if (ret) {
3242
                gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_QUORUM_CHECK_FAIL,
3243
                       "Quorum check"
3244
                       "failed during snapshot create command");
3245
                goto out;
3246
            }
3247
            break;
3248
        case GF_SNAP_OPTION_TYPE_CLONE:
3249
            ret = glusterd_snap_quorum_check_for_clone(dict, !snap_volume,
3250
                                                       op_errstr, op_errno);
3251
            if (ret) {
3252
                gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_QUORUM_CHECK_FAIL,
3253
                       "Quorum check"
3254
                       "failed during snapshot clone command");
3255
                goto out;
3256
            }
3257
            break;
3258
        case GF_SNAP_OPTION_TYPE_DELETE:
3259
        case GF_SNAP_OPTION_TYPE_RESTORE:
3260
            if (!does_gd_meet_server_quorum(this)) {
3261
                ret = -1;
3262
                gf_msg(this->name, GF_LOG_WARNING, 0,
3263
                       GD_MSG_SERVER_QUORUM_NOT_MET, "%s", err_str);
3264
                *op_errstr = gf_strdup(err_str);
3265
                *op_errno = EG_NODEDWN;
3266
                goto out;
3267
            }
3268

3269
            gf_msg_debug(this->name, 0,
3270
                         "glusterds are in "
3271
                         "quorum");
3272
            break;
3273
        default:
3274
            break;
3275
    }
3276

3277
    ret = 0;
3278

3279
out:
3280
    return ret;
3281
}
3282

3283
int
3284
glusterd_is_path_mounted(const char *path)
3285
{
3286
    FILE *mtab = NULL;
3287
    struct mntent *part = NULL;
3288
    int is_mounted = 0;
3289

3290
    if ((mtab = setmntent("/etc/mtab", "r")) != NULL) {
3291
        while ((part = getmntent(mtab)) != NULL) {
3292
            if ((part->mnt_fsname != NULL) &&
3293
                (strcmp(part->mnt_dir, path)) == 0) {
3294
                is_mounted = 1;
3295
                break;
3296
            }
3297
        }
3298
        endmntent(mtab);
3299
    }
3300
    return is_mounted;
3301
}
3302
/* This function will do unmount for snaps.
3303
 */
3304
int32_t
3305
glusterd_snap_unmount(xlator_t *this, glusterd_volinfo_t *volinfo)
3306
{
3307
    char *brick_mount_path = NULL;
3308
    glusterd_brickinfo_t *brickinfo = NULL;
3309
    int32_t ret = -1;
3310
    int retry_count = 0;
3311
    int brick_count = -1;
3312
    struct glusterd_snap_ops *snap_ops = NULL;
3313

3314
    GF_ASSERT(volinfo);
3315

3316
    glusterd_snapshot_plugin_by_name(volinfo->snap_plugin, &snap_ops);
3317

3318
    cds_list_for_each_entry(brickinfo, &volinfo->bricks, brick_list)
3319
    {
3320
        brick_count++;
3321

3322
        /* If the brick is not of this node, we continue */
3323
        if (gf_uuid_compare(brickinfo->uuid, MY_UUID)) {
3324
            continue;
3325
        }
3326
        /* If snapshot is pending, we continue */
3327
        if (brickinfo->snap_status == -1) {
3328
            continue;
3329
        }
3330

3331
        ret = glusterd_find_brick_mount_path(brickinfo->path,
3332
                                             &brick_mount_path);
3333
        if (ret) {
3334
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_BRK_MNTPATH_GET_FAIL,
3335
                   "Failed to find brick_mount_path for %s", brickinfo->path);
3336
            goto out;
3337
        }
3338

3339
        /* unmount cannot be done when the brick process is still in
3340
         * the process of shutdown, so give three re-tries
3341
         */
3342
        retry_count = 0;
3343
        while (retry_count <= 2) {
3344
            retry_count++;
3345
            ret = snap_ops->deactivate(brickinfo, volinfo->snapshot->snapname,
3346
                                       volinfo->volname, brick_count);
3347
            if (!ret)
3348
                break;
3349
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_GLUSTERD_UMOUNT_FAIL,
3350
                   "umount failed "
3351
                   "for path %s (brick: %s): %s. Retry(%d)",
3352
                   brick_mount_path, brickinfo->path, strerror(errno),
3353
                   retry_count);
3354
            sleep(3);
3355
        }
3356
    }
3357

3358
out:
3359
    if (brick_mount_path)
3360
        GF_FREE(brick_mount_path);
3361

3362
    return ret;
3363
}
3364

3365
int32_t
3366
glusterd_copy_file(const char *source, const char *destination)
3367
{
3368
    int32_t ret = -1;
3369
    xlator_t *this = THIS;
3370
    char buffer[1024] = "";
3371
    int src_fd = -1;
3372
    int dest_fd = -1;
3373
    int read_len = -1;
3374
    struct stat stbuf = {
3375
        0,
3376
    };
3377
    mode_t dest_mode = 0;
3378

3379
    GF_ASSERT(source);
3380
    GF_ASSERT(destination);
3381

3382
    /* Here is stat is made to get the file permission of source file*/
3383
    ret = sys_lstat(source, &stbuf);
3384
    if (ret) {
3385
        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
3386
               "%s not found", source);
3387
        goto out;
3388
    }
3389

3390
    dest_mode = stbuf.st_mode & 0777;
3391

3392
    src_fd = open(source, O_RDONLY);
3393
    if (src_fd == -1) {
3394
        ret = -1;
3395
        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
3396
               "Unable to open file %s", source);
3397
        goto out;
3398
    }
3399

3400
    dest_fd = sys_creat(destination, dest_mode);
3401
    if (dest_fd < 0) {
3402
        ret = -1;
3403
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_FILE_OP_FAILED,
3404
               "Unble to open a file %s", destination);
3405
        goto out;
3406
    }
3407

3408
    do {
3409
        ret = sys_read(src_fd, buffer, sizeof(buffer));
3410
        if (ret == -1) {
3411
            gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
3412
                   "Error reading file "
3413
                   "%s",
3414
                   source);
3415
            goto out;
3416
        }
3417
        read_len = ret;
3418
        if (read_len == 0)
3419
            break;
3420

3421
        ret = sys_write(dest_fd, buffer, read_len);
3422
        if (ret != read_len) {
3423
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_FILE_OP_FAILED,
3424
                   "Writing in "
3425
                   "file %s failed with error %s",
3426
                   destination, strerror(errno));
3427
            goto out;
3428
        }
3429
    } while (ret > 0);
3430
out:
3431
    if (src_fd != -1)
3432
        sys_close(src_fd);
3433

3434
    if (dest_fd > 0)
3435
        sys_close(dest_fd);
3436
    return ret;
3437
}
3438

3439
int32_t
3440
glusterd_copy_folder(const char *source, const char *destination)
3441
{
3442
    int32_t ret = -1;
3443
    xlator_t *this = THIS;
3444
    DIR *dir_ptr = NULL;
3445
    struct dirent *entry = NULL;
3446
    struct dirent scratch[2] = {
3447
        {
3448
            0,
3449
        },
3450
    };
3451
    char src_path[PATH_MAX] = {
3452
        0,
3453
    };
3454
    char dest_path[PATH_MAX] = {
3455
        0,
3456
    };
3457

3458
    GF_ASSERT(source);
3459
    GF_ASSERT(destination);
3460

3461
    dir_ptr = sys_opendir(source);
3462
    if (!dir_ptr) {
3463
        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DIR_OP_FAILED,
3464
               "Unable to open %s", source);
3465
        goto out;
3466
    }
3467

3468
    for (;;) {
3469
        errno = 0;
3470
        entry = sys_readdir(dir_ptr, scratch);
3471
        if (!entry || errno != 0)
3472
            break;
3473

3474
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
3475
            continue;
3476
        ret = snprintf(src_path, sizeof(src_path), "%s/%s", source,
3477
                       entry->d_name);
3478
        if (ret < 0) {
3479
            gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
3480
            goto out;
3481
        }
3482

3483
        ret = snprintf(dest_path, sizeof(dest_path), "%s/%s", destination,
3484
                       entry->d_name);
3485
        if (ret < 0) {
3486
            gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
3487
            goto out;
3488
        }
3489

3490
        ret = glusterd_copy_file(src_path, dest_path);
3491
        if (ret) {
3492
            gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
3493
                   "Could not copy "
3494
                   "%s to %s",
3495
                   src_path, dest_path);
3496
            goto out;
3497
        }
3498
    }
3499
out:
3500
    if (dir_ptr)
3501
        (void)sys_closedir(dir_ptr);
3502

3503
    return ret;
3504
}
3505

3506
int32_t
3507
glusterd_get_geo_rep_session(char *secondary_key, char *origin_volname,
3508
                             dict_t *gsync_secondaries_dict, char *session,
3509
                             char *secondary)
3510
{
3511
    int32_t ret = -1;
3512
    int32_t len = 0;
3513
    char *token = NULL;
3514
    char *tok = NULL;
3515
    char *temp = NULL;
3516
    char *ip = NULL;
3517
    char *ip_i = NULL;
3518
    char *ip_temp = NULL;
3519
    char *buffer = NULL;
3520
    char *secondary_temp = NULL;
3521
    char *save_ptr = NULL;
3522

3523
    GF_ASSERT(secondary_key);
3524
    GF_ASSERT(origin_volname);
3525
    GF_ASSERT(gsync_secondaries_dict);
3526

3527
    ret = dict_get_str(gsync_secondaries_dict, secondary_key, &buffer);
3528
    if (ret) {
3529
        gf_msg(THIS->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3530
               "Failed to "
3531
               "get value for key %s",
3532
               secondary_key);
3533
        goto out;
3534
    }
3535

3536
    temp = gf_strdup(buffer);
3537
    if (!temp) {
3538
        ret = -1;
3539
        goto out;
3540
    }
3541

3542
    /* geo-rep session string format being parsed:
3543
     * "primary_node_uuid:ssh://secondary_host::secondary_vol:secondary_voluuid"
3544
     */
3545
    token = strtok_r(temp, "/", &save_ptr);
3546

3547
    token = strtok_r(NULL, ":", &save_ptr);
3548
    if (!token) {
3549
        ret = -1;
3550
        goto out;
3551
    }
3552
    token++;
3553

3554
    ip = gf_strdup(token);
3555
    if (!ip) {
3556
        ret = -1;
3557
        goto out;
3558
    }
3559
    ip_i = ip;
3560

3561
    token = strtok_r(NULL, ":", &save_ptr);
3562
    if (!token) {
3563
        ret = -1;
3564
        goto out;
3565
    }
3566

3567
    secondary_temp = gf_strdup(token);
3568
    if (!secondary) {
3569
        ret = -1;
3570
        goto out;
3571
    }
3572

3573
    /* If 'ip' has 'root@secondaryhost', point to 'secondaryhost' as
3574
     * working directory for root users are created without
3575
     * 'root@' */
3576
    ip_temp = gf_strdup(ip);
3577
    tok = strtok_r(ip_temp, "@", &save_ptr);
3578
    len = strlen(tok);
3579
    tok = strtok_r(NULL, "@", &save_ptr);
3580
    if (tok != NULL)
3581
        ip_i = ip + len + 1;
3582

3583
    ret = snprintf(session, PATH_MAX, "%s_%s_%s", origin_volname, ip_i,
3584
                   secondary_temp);
3585
    if (ret < 0) /* Negative value is an error */
3586
        goto out;
3587

3588
    ret = snprintf(secondary, PATH_MAX, "%s::%s", ip, secondary_temp);
3589
    if (ret < 0) {
3590
        goto out;
3591
    }
3592

3593
    ret = 0; /* Success */
3594

3595
out:
3596
    if (temp)
3597
        GF_FREE(temp);
3598

3599
    if (ip)
3600
        GF_FREE(ip);
3601

3602
    if (ip_temp)
3603
        GF_FREE(ip_temp);
3604

3605
    if (secondary_temp)
3606
        GF_FREE(secondary_temp);
3607

3608
    return ret;
3609
}
3610

3611
int32_t
3612
glusterd_copy_quota_files(glusterd_volinfo_t *src_vol,
3613
                          glusterd_volinfo_t *dest_vol,
3614
                          gf_boolean_t *conf_present)
3615
{
3616
    int32_t ret = -1;
3617
    char src_dir[PATH_MAX] = "";
3618
    char dest_dir[PATH_MAX] = "";
3619
    char src_path[PATH_MAX] = "";
3620
    char dest_path[PATH_MAX] = "";
3621
    xlator_t *this = THIS;
3622
    glusterd_conf_t *priv = NULL;
3623
    struct stat stbuf = {
3624
        0,
3625
    };
3626

3627
    priv = this->private;
3628
    GF_ASSERT(priv);
3629

3630
    GF_ASSERT(src_vol);
3631
    GF_ASSERT(dest_vol);
3632

3633
    GLUSTERD_GET_VOLUME_DIR(src_dir, src_vol, priv);
3634

3635
    GLUSTERD_GET_VOLUME_DIR(dest_dir, dest_vol, priv);
3636

3637
    ret = snprintf(src_path, sizeof(src_path), "%s/quota.conf", src_dir);
3638
    if (ret < 0) {
3639
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
3640
        goto out;
3641
    }
3642

3643
    /* quota.conf is not present if quota is not enabled, Hence ignoring
3644
     * the absence of this file
3645
     */
3646
    ret = sys_lstat(src_path, &stbuf);
3647
    if (ret) {
3648
        ret = 0;
3649
        gf_msg_debug(this->name, 0, "%s not found", src_path);
3650
        goto out;
3651
    }
3652

3653
    ret = snprintf(dest_path, sizeof(dest_path), "%s/quota.conf", dest_dir);
3654
    if (ret < 0) {
3655
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
3656
        goto out;
3657
    }
3658

3659
    ret = glusterd_copy_file(src_path, dest_path);
3660
    if (ret) {
3661
        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
3662
               "Failed to copy %s in %s", src_path, dest_path);
3663
        goto out;
3664
    }
3665

3666
    ret = snprintf(src_path, sizeof(src_path), "%s/quota.cksum", src_dir);
3667
    if (ret < 0)
3668
        goto out;
3669

3670
    /* if quota.conf is present, quota.cksum has to be present. *
3671
     * Fail snapshot operation if file is absent                *
3672
     */
3673
    ret = sys_lstat(src_path, &stbuf);
3674
    if (ret) {
3675
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_FILE_NOT_FOUND,
3676
               "%s not found", src_path);
3677
        goto out;
3678
    }
3679

3680
    ret = snprintf(dest_path, sizeof(dest_path), "%s/quota.cksum", dest_dir);
3681
    if (ret < 0) {
3682
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
3683
        goto out;
3684
    }
3685

3686
    ret = glusterd_copy_file(src_path, dest_path);
3687
    if (ret) {
3688
        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
3689
               "Failed to copy %s in %s", src_path, dest_path);
3690
        goto out;
3691
    }
3692

3693
    *conf_present = _gf_true;
3694
out:
3695
    return ret;
3696
}
3697

3698
/* *
3699
 * Here there are two possibilities, either destination is snaphot or
3700
 * clone. In the case of snapshot nfs_ganesha export file will be copied
3701
 * to snapdir. If it is clone , then new export file will be created for
3702
 * the clone in the GANESHA_EXPORT_DIRECTORY, replacing occurences of
3703
 * volname with clonename
3704
 */
3705
int
3706
glusterd_copy_nfs_ganesha_file(glusterd_volinfo_t *src_vol,
3707
                               glusterd_volinfo_t *dest_vol)
3708
{
3709
    int32_t ret = -1;
3710
    char snap_dir[PATH_MAX] = {
3711
        0,
3712
    };
3713
    char src_path[PATH_MAX] = {
3714
        0,
3715
    };
3716
    char dest_path[PATH_MAX] = {
3717
        0,
3718
    };
3719
    char buffer[BUFSIZ] = {
3720
        0,
3721
    };
3722
    char *find_ptr = NULL;
3723
    char *buff_ptr = NULL;
3724
    char *tmp_ptr = NULL;
3725
    xlator_t *this = THIS;
3726
    glusterd_conf_t *priv = NULL;
3727
    struct stat stbuf = {
3728
        0,
3729
    };
3730
    FILE *src = NULL;
3731
    FILE *dest = NULL;
3732

3733
    priv = this->private;
3734
    GF_VALIDATE_OR_GOTO(this->name, priv, out);
3735

3736
    GF_VALIDATE_OR_GOTO(this->name, src_vol, out);
3737
    GF_VALIDATE_OR_GOTO(this->name, dest_vol, out);
3738

3739
    if (glusterd_check_ganesha_export(src_vol) == _gf_false) {
3740
        gf_msg_debug(this->name, 0,
3741
                     "%s is not exported via "
3742
                     "NFS-Ganesha. Skipping copy of export conf.",
3743
                     src_vol->volname);
3744
        ret = 0;
3745
        goto out;
3746
    }
3747

3748
    if (src_vol->is_snap_volume) {
3749
        GLUSTERD_GET_SNAP_DIR(snap_dir, src_vol->snapshot, priv);
3750
        ret = snprintf(src_path, PATH_MAX, "%s/export.%s.conf", snap_dir,
3751
                       src_vol->snapshot->snapname);
3752
    } else {
3753
        ret = snprintf(src_path, PATH_MAX, "%s/export.%s.conf",
3754
                       GANESHA_EXPORT_DIRECTORY, src_vol->volname);
3755
    }
3756
    if (ret < 0 || ret >= PATH_MAX)
3757
        goto out;
3758

3759
    ret = sys_lstat(src_path, &stbuf);
3760
    if (ret) {
3761
        /*
3762
         * This code path is hit, only when the src_vol is being *
3763
         * exported via NFS-Ganesha. So if the conf file is not  *
3764
         * available, we fail the snapshot operation.            *
3765
         */
3766
        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
3767
               "Stat on %s failed with %s", src_path, strerror(errno));
3768
        goto out;
3769
    }
3770

3771
    if (dest_vol->is_snap_volume) {
3772
        memset(snap_dir, 0, PATH_MAX);
3773
        GLUSTERD_GET_SNAP_DIR(snap_dir, dest_vol->snapshot, priv);
3774
        ret = snprintf(dest_path, sizeof(dest_path), "%s/export.%s.conf",
3775
                       snap_dir, dest_vol->snapshot->snapname);
3776
        if (ret < 0)
3777
            goto out;
3778

3779
        ret = glusterd_copy_file(src_path, dest_path);
3780
        if (ret) {
3781
            gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
3782
                   "Failed to copy %s in %s", src_path, dest_path);
3783
            goto out;
3784
        }
3785

3786
    } else {
3787
        ret = snprintf(dest_path, sizeof(dest_path), "%s/export.%s.conf",
3788
                       GANESHA_EXPORT_DIRECTORY, dest_vol->volname);
3789
        if (ret < 0)
3790
            goto out;
3791

3792
        src = fopen(src_path, "r");
3793
        dest = fopen(dest_path, "w");
3794

3795
        if (!src || !dest) {
3796
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_FILE_OP_FAILED,
3797
                   "Failed to open %s", dest ? src_path : dest_path);
3798
            ret = -1;
3799
            goto out;
3800
        }
3801

3802
        /* *
3803
         * if the source volume is snapshot, the export conf file
3804
         * consists of orginal volname
3805
         */
3806
        if (src_vol->is_snap_volume)
3807
            find_ptr = gf_strdup(src_vol->parent_volname);
3808
        else
3809
            find_ptr = gf_strdup(src_vol->volname);
3810

3811
        if (!find_ptr)
3812
            goto out;
3813

3814
        /* Replacing volname with clonename */
3815
        while (fgets(buffer, BUFSIZ, src)) {
3816
            buff_ptr = buffer;
3817
            while ((tmp_ptr = strstr(buff_ptr, find_ptr))) {
3818
                while (buff_ptr < tmp_ptr)
3819
                    fputc((int)*buff_ptr++, dest);
3820
                fputs(dest_vol->volname, dest);
3821
                buff_ptr += strlen(find_ptr);
3822
            }
3823
            fputs(buff_ptr, dest);
3824
            memset(buffer, 0, BUFSIZ);
3825
        }
3826
    }
3827
out:
3828
    if (src)
3829
        fclose(src);
3830
    if (dest)
3831
        fclose(dest);
3832
    if (find_ptr)
3833
        GF_FREE(find_ptr);
3834

3835
    return ret;
3836
}
3837

3838
int32_t
3839
glusterd_restore_geo_rep_files(glusterd_volinfo_t *snap_vol)
3840
{
3841
    int32_t ret = -1;
3842
    char src_path[PATH_MAX] = "";
3843
    char dest_path[PATH_MAX] = "";
3844
    xlator_t *this = THIS;
3845
    char *origin_volname = NULL;
3846
    glusterd_volinfo_t *origin_vol = NULL;
3847
    int i = 0;
3848
    char key[32] = "";
3849
    char session[PATH_MAX] = "";
3850
    char secondary[PATH_MAX] = "";
3851
    char snapgeo_dir[PATH_MAX] = "";
3852
    glusterd_conf_t *priv = NULL;
3853

3854
    priv = this->private;
3855
    GF_ASSERT(priv);
3856

3857
    GF_ASSERT(snap_vol);
3858

3859
    origin_volname = gf_strdup(snap_vol->parent_volname);
3860
    if (!origin_volname) {
3861
        ret = -1;
3862
        goto out;
3863
    }
3864

3865
    ret = glusterd_volinfo_find(origin_volname, &origin_vol);
3866
    if (ret) {
3867
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOL_NOT_FOUND,
3868
               "Unable to fetch "
3869
               "volinfo for volname %s",
3870
               origin_volname);
3871
        goto out;
3872
    }
3873

3874
    for (i = 1; i <= snap_vol->gsync_secondaries->count; i++) {
3875
        ret = snprintf(key, sizeof(key), "secondary%d", i);
3876
        if (ret < 0) {
3877
            goto out;
3878
        }
3879

3880
        /* "origin_vol" is used here because geo-replication saves
3881
         * the session in the form of primary_ip_secondary.
3882
         * As we need the primary volume to be same even after
3883
         * restore, we are passing the origin volume name.
3884
         *
3885
         * "snap_vol->gsync_secondaries" contain the secondary information
3886
         * when the snapshot was taken, hence we have to restore all
3887
         * those secondaries information when we do snapshot restore.
3888
         */
3889
        ret = glusterd_get_geo_rep_session(key, origin_vol->volname,
3890
                                           snap_vol->gsync_secondaries, session,
3891
                                           secondary);
3892
        if (ret) {
3893
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_GEOREP_GET_FAILED,
3894
                   "Failed to get geo-rep session");
3895
            goto out;
3896
        }
3897

3898
        GLUSTERD_GET_SNAP_GEO_REP_DIR(snapgeo_dir, snap_vol->snapshot, priv);
3899
        ret = snprintf(src_path, sizeof(src_path), "%s/%s", snapgeo_dir,
3900
                       session);
3901
        if (ret < 0)
3902
            goto out;
3903

3904
        ret = snprintf(dest_path, sizeof(dest_path), "%s/%s/%s", priv->workdir,
3905
                       GEOREP, session);
3906
        if (ret < 0)
3907
            goto out;
3908

3909
        ret = glusterd_copy_folder(src_path, dest_path);
3910
        if (ret) {
3911
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DIR_OP_FAILED,
3912
                   "Could not copy "
3913
                   "%s to %s",
3914
                   src_path, dest_path);
3915
            goto out;
3916
        }
3917
    }
3918
out:
3919
    if (origin_volname)
3920
        GF_FREE(origin_volname);
3921

3922
    return ret;
3923
}
3924

3925
int
3926
glusterd_restore_nfs_ganesha_file(glusterd_volinfo_t *src_vol,
3927
                                  glusterd_snap_t *snap)
3928
{
3929
    int32_t ret = -1;
3930
    char snap_dir[PATH_MAX] = "";
3931
    char src_path[PATH_MAX] = "";
3932
    char dest_path[PATH_MAX] = "";
3933
    xlator_t *this = THIS;
3934
    glusterd_conf_t *priv = NULL;
3935
    struct stat stbuf = {
3936
        0,
3937
    };
3938

3939
    priv = this->private;
3940
    GF_VALIDATE_OR_GOTO(this->name, priv, out);
3941

3942
    GF_VALIDATE_OR_GOTO(this->name, src_vol, out);
3943
    GF_VALIDATE_OR_GOTO(this->name, snap, out);
3944

3945
    GLUSTERD_GET_SNAP_DIR(snap_dir, snap, priv);
3946

3947
    ret = snprintf(src_path, sizeof(src_path), "%s/export.%s.conf", snap_dir,
3948
                   snap->snapname);
3949
    if (ret < 0) {
3950
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
3951
        goto out;
3952
    }
3953

3954
    ret = sys_lstat(src_path, &stbuf);
3955
    if (ret) {
3956
        if (errno == ENOENT) {
3957
            ret = 0;
3958
            gf_msg_debug(this->name, errno, "%s not found", src_path);
3959
        } else
3960
            gf_msg(this->name, GF_LOG_WARNING, errno, GD_MSG_FILE_OP_FAILED,
3961
                   "Stat on %s failed with %s", src_path, strerror(errno));
3962
        goto out;
3963
    }
3964

3965
    ret = snprintf(dest_path, sizeof(dest_path), "%s/export.%s.conf",
3966
                   GANESHA_EXPORT_DIRECTORY, src_vol->volname);
3967
    if (ret < 0) {
3968
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
3969
        goto out;
3970
    }
3971

3972
    ret = glusterd_copy_file(src_path, dest_path);
3973
    if (ret)
3974
        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
3975
               "Failed to copy %s in %s", src_path, dest_path);
3976

3977
out:
3978
    return ret;
3979
}
3980

3981
/* Snapd functions */
3982
int
3983
glusterd_is_snapd_enabled(glusterd_volinfo_t *volinfo)
3984
{
3985
    int ret = 0;
3986

3987
    ret = dict_get_str_boolean(volinfo->dict, "features.uss", -2);
3988
    if (ret == -2) {
3989
        gf_msg_debug(THIS->name, 0,
3990
                     "Key features.uss not "
3991
                     "present in the dict for volume %s",
3992
                     volinfo->volname);
3993
        ret = 0;
3994

3995
    } else if (ret == -1) {
3996
        gf_msg(THIS->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
3997
               "Failed to get 'features.uss'"
3998
               " from dict for volume %s",
3999
               volinfo->volname);
4000
    }
4001

4002
    return ret;
4003
}
4004

4005
int32_t
4006
glusterd_is_snap_soft_limit_reached(glusterd_volinfo_t *volinfo, dict_t *dict)
4007
{
4008
    int32_t ret = -1;
4009
    uint64_t opt_max_hard = GLUSTERD_SNAPS_MAX_HARD_LIMIT;
4010
    uint64_t opt_max_soft = GLUSTERD_SNAPS_DEF_SOFT_LIMIT_PERCENT;
4011
    uint64_t limit = 0;
4012
    int auto_delete = 0;
4013
    uint64_t effective_max_limit = 0;
4014
    xlator_t *this = THIS;
4015
    glusterd_conf_t *priv = NULL;
4016

4017
    GF_ASSERT(volinfo);
4018
    GF_ASSERT(dict);
4019

4020
    priv = this->private;
4021
    GF_ASSERT(priv);
4022

4023
    /* config values snap-max-hard-limit and snap-max-soft-limit are
4024
     * optional and hence we are not erroring out if values are not
4025
     * present
4026
     */
4027
    gd_get_snap_conf_values_if_present(priv->opts, &opt_max_hard,
4028
                                       &opt_max_soft);
4029

4030
    /* "auto-delete" might not be set by user explicitly,
4031
     * in that case it's better to consider the default value.
4032
     * Hence not erroring out if Key is not found.
4033
     */
4034
    auto_delete = dict_get_str_boolean(
4035
        priv->opts, GLUSTERD_STORE_KEY_SNAP_AUTO_DELETE, _gf_false);
4036

4037
    if (volinfo->snap_max_hard_limit < opt_max_hard)
4038
        effective_max_limit = volinfo->snap_max_hard_limit;
4039
    else
4040
        effective_max_limit = opt_max_hard;
4041

4042
    limit = (opt_max_soft * effective_max_limit) / 100;
4043

4044
    if (volinfo->snap_count >= limit && auto_delete != _gf_true) {
4045
        gf_msg(this->name, GF_LOG_WARNING, 0, GD_MSG_SOFT_LIMIT_REACHED,
4046
               "Soft-limit "
4047
               "(value = %" PRIu64
4048
               ") of volume %s is reached. "
4049
               "Snapshot creation is not possible once effective "
4050
               "hard-limit (value = %" PRIu64 ") is reached.",
4051
               limit, volinfo->volname, effective_max_limit);
4052

4053
        ret = dict_set_int8(dict, "soft-limit-reach", _gf_true);
4054
        if (ret) {
4055
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
4056
                   "Failed to "
4057
                   "set soft limit exceed flag in "
4058
                   "response dictionary");
4059
        }
4060

4061
        goto out;
4062
    }
4063
    ret = 0;
4064
out:
4065
    return ret;
4066
}
4067

4068
/* This function initializes the parameter sys_hard_limit,
4069
 * sys_soft_limit and auto_delete value to the value set
4070
 * in dictionary, If value is not present then it is
4071
 * initialized to default values. Hence this function does not
4072
 * return any values.
4073
 */
4074
void
4075
gd_get_snap_conf_values_if_present(dict_t *dict, uint64_t *sys_hard_limit,
4076
                                   uint64_t *sys_soft_limit)
4077
{
4078
    xlator_t *this = THIS;
4079

4080
    GF_ASSERT(dict);
4081

4082
    /* "snap-max-hard-limit" might not be set by user explicitly,
4083
     * in that case it's better to consider the default value.
4084
     * Hence not erroring out if Key is not found.
4085
     */
4086
    if (dict_get_uint64(dict, GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT,
4087
                        sys_hard_limit)) {
4088
        gf_msg_debug(this->name, 0,
4089
                     "%s is not present in"
4090
                     "dictionary",
4091
                     GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT);
4092
    }
4093

4094
    /* "snap-max-soft-limit" might not be set by user explicitly,
4095
     * in that case it's better to consider the default value.
4096
     * Hence not erroring out if Key is not found.
4097
     */
4098
    if (dict_get_uint64(dict, GLUSTERD_STORE_KEY_SNAP_MAX_SOFT_LIMIT,
4099
                        sys_soft_limit)) {
4100
        gf_msg_debug(this->name, 0,
4101
                     "%s is not present in"
4102
                     "dictionary",
4103
                     GLUSTERD_STORE_KEY_SNAP_MAX_SOFT_LIMIT);
4104
    }
4105
}
4106

4107
int
4108
glusterd_get_snap_status_str(glusterd_snap_t *snapinfo, char *snap_status_str)
4109
{
4110
    int ret = -1;
4111

4112
    GF_VALIDATE_OR_GOTO(THIS->name, snapinfo, out);
4113
    GF_VALIDATE_OR_GOTO(THIS->name, snap_status_str, out);
4114

4115
    switch (snapinfo->snap_status) {
4116
        case GD_SNAP_STATUS_NONE:
4117
            sprintf(snap_status_str, "%s", "none");
4118
            break;
4119
        case GD_SNAP_STATUS_INIT:
4120
            sprintf(snap_status_str, "%s", "init");
4121
            break;
4122
        case GD_SNAP_STATUS_IN_USE:
4123
            sprintf(snap_status_str, "%s", "in_use");
4124
            break;
4125
        case GD_SNAP_STATUS_DECOMMISSION:
4126
            sprintf(snap_status_str, "%s", "decommissioned");
4127
            break;
4128
        case GD_SNAP_STATUS_UNDER_RESTORE:
4129
            sprintf(snap_status_str, "%s", "under_restore");
4130
            break;
4131
        case GD_SNAP_STATUS_RESTORED:
4132
            sprintf(snap_status_str, "%s", "restored");
4133
            break;
4134
        default:
4135
            goto out;
4136
    }
4137
    ret = 0;
4138
out:
4139
    return ret;
4140
}
4141

4142
void
4143
glusterd_snapshot_plugin_by_name(char *name,
4144
                                 struct glusterd_snap_ops **snap_ops)
4145
{
4146
    xlator_t *this = NULL;
4147

4148
    this = THIS;
4149

4150
    if (strcmp(name, "LVM") == 0)
4151
        *snap_ops = &lvm_snap_ops;
4152
    else if (strcmp(name, "ZFS") == 0)
4153
        *snap_ops = &zfs_snap_ops;
4154

4155
    gf_msg_debug(this->name, 0, "Loaded Snapshot plugin %s", name);
4156
}
4157

4158
gf_boolean_t
4159
glusterd_snapshot_probe(char *brick_path, glusterd_brickinfo_t *brickinfo)
4160
{
4161
    struct glusterd_snap_ops *glusterd_snap_backend[] = {
4162
        &lvm_snap_ops,
4163
        &zfs_snap_ops,
4164
        0,
4165
    };
4166
    xlator_t *this = NULL;
4167
    int i = 0;
4168

4169
    this = THIS;
4170

4171
    if (brickinfo->snap)
4172
        return _gf_true;
4173

4174
    gf_log(this->name, GF_LOG_INFO, "Probing brick %s for snapshot support",
4175
           brick_path);
4176
    for (i = 0; glusterd_snap_backend[i]; i++) {
4177
        if (glusterd_snap_backend[i]->probe(brick_path)) {
4178
            gf_log(this->name, GF_LOG_INFO, "%s backend detected",
4179
                   glusterd_snap_backend[i]->name);
4180
            brickinfo->snap = glusterd_snap_backend[i];
4181
            return _gf_true;
4182
        }
4183
        gf_log(this->name, GF_LOG_DEBUG, "not a %s backend",
4184
               glusterd_snap_backend[i]->name);
4185
    }
4186

4187
    return _gf_false;
4188
}
4189

4190
/*
4191
  Verify availability of a command
4192
*/
4193

4194
gf_boolean_t
4195
glusterd_is_cmd_available(char *cmd)
4196
{
4197
    int32_t ret = 0;
4198
    struct stat buf = {
4199
        0,
4200
    };
4201

4202
    if (!cmd)
4203
        return _gf_false;
4204

4205
    ret = sys_stat(cmd, &buf);
4206
    if (ret != 0) {
4207
        gf_msg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
4208
               "stat fails on %s, exiting. (errno = %d (%s))", cmd, errno,
4209
               strerror(errno));
4210
        return _gf_false;
4211
    }
4212

4213
    if ((!ret) && (!S_ISREG(buf.st_mode))) {
4214
        gf_msg(THIS->name, GF_LOG_CRITICAL, EINVAL, GD_MSG_COMMAND_NOT_FOUND,
4215
               "Provided command %s is not a regular file,"
4216
               "exiting",
4217
               cmd);
4218
        return _gf_false;
4219
    }
4220

4221
    if ((!ret) && (!(buf.st_mode & S_IXUSR))) {
4222
        gf_msg(THIS->name, GF_LOG_CRITICAL, 0, GD_MSG_NO_EXEC_PERMS,
4223
               "Provided command %s has no exec permissions,"
4224
               "exiting",
4225
               cmd);
4226
        return _gf_false;
4227
    }
4228

4229
    return _gf_true;
4230
}
4231

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

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

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

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