Revision 212a5a8f

b/block.c
5094 5094
    return bs->drv->bdrv_amend_options(bs, options);
5095 5095
}
5096 5096

  
5097
ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs)
5097
/* Used to recurse on single child block filters.
5098
 * Single child block filter will store their child in bs->file.
5099
 */
5100
bool bdrv_generic_is_first_non_filter(BlockDriverState *bs,
5101
                                      BlockDriverState *candidate)
5098 5102
{
5099
    if (bs->drv->bdrv_check_ext_snapshot) {
5100
        return bs->drv->bdrv_check_ext_snapshot(bs);
5103
    if (!bs->drv) {
5104
        return false;
5105
    }
5106

  
5107
    if (!bs->drv->authorizations[BS_IS_A_FILTER]) {
5108
        if (bs == candidate) {
5109
            return true;
5110
        } else {
5111
            return false;
5112
        }
5113
    }
5114

  
5115
    if (!bs->drv->authorizations[BS_FILTER_PASS_DOWN]) {
5116
        return false;
5101 5117
    }
5102 5118

  
5103
    if (bs->file && bs->file->drv && bs->file->drv->bdrv_check_ext_snapshot) {
5104
        return bs->file->drv->bdrv_check_ext_snapshot(bs);
5119
    if (!bs->file) {
5120
        return false;
5121
    }
5122

  
5123
    return bdrv_recurse_is_first_non_filter(bs->file, candidate);
5124
}
5125

  
5126
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
5127
                                      BlockDriverState *candidate)
5128
{
5129
    if (bs->drv && bs->drv->bdrv_recurse_is_first_non_filter) {
5130
        return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate);
5105 5131
    }
5106 5132

  
5107
    /* external snapshots are allowed by default */
5108
    return EXT_SNAPSHOT_ALLOWED;
5133
    return bdrv_generic_is_first_non_filter(bs, candidate);
5109 5134
}
5110 5135

  
5111
ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs)
5136
/* This function checks if the candidate is the first non filter bs down it's
5137
 * bs chain. Since we don't have pointers to parents it explore all bs chains
5138
 * from the top. Some filters can choose not to pass down the recursion.
5139
 */
5140
bool bdrv_is_first_non_filter(BlockDriverState *candidate)
5112 5141
{
5113
    return EXT_SNAPSHOT_FORBIDDEN;
5142
    BlockDriverState *bs;
5143

  
5144
    /* walk down the bs forest recursively */
5145
    QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
5146
        bool perm;
5147

  
5148
        if (!bs->file) {
5149
            continue;
5150
        }
5151

  
5152
        perm = bdrv_recurse_is_first_non_filter(bs->file, candidate);
5153

  
5154
        /* candidate is the first non filter */
5155
        if (perm) {
5156
            return true;
5157
        }
5158
    }
5159

  
5160
    return false;
5114 5161
}
b/block/blkverify.c
404 404
    .bdrv_aio_writev        = blkverify_aio_writev,
405 405
    .bdrv_aio_flush         = blkverify_aio_flush,
406 406

  
407
    .bdrv_check_ext_snapshot = bdrv_check_ext_snapshot_forbidden,
407
    .authorizations         = { true, false },
408 408
};
409 409

  
410 410
static void bdrv_blkverify_init(void)
b/blockdev.c
1243 1243
        }
1244 1244
    }
1245 1245

  
1246
    if (bdrv_check_ext_snapshot(state->old_bs) != EXT_SNAPSHOT_ALLOWED) {
1246
    if (!bdrv_is_first_non_filter(state->old_bs)) {
1247 1247
        error_set(errp, QERR_FEATURE_DISABLED, "snapshot");
1248 1248
        return;
1249 1249
    }
b/include/block/block.h
287 287
/* external snapshots */
288 288

  
289 289
typedef enum {
290
    EXT_SNAPSHOT_ALLOWED,
291
    EXT_SNAPSHOT_FORBIDDEN,
292
} ExtSnapshotPerm;
293

  
294
/* return EXT_SNAPSHOT_ALLOWED if external snapshot is allowed
295
 * return EXT_SNAPSHOT_FORBIDDEN if external snapshot is forbidden
296
 */
297
ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs);
298
/* helper used to forbid external snapshots like in blkverify */
299
ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs);
290
    BS_IS_A_FILTER,
291
    BS_FILTER_PASS_DOWN,
292
    BS_AUTHORIZATION_COUNT,
293
} BsAuthorization;
294

  
295
bool bdrv_generic_is_first_non_filter(BlockDriverState *bs,
296
                                      BlockDriverState *candidate);
297
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
298
                                      BlockDriverState *candidate);
299
bool bdrv_is_first_non_filter(BlockDriverState *candidate);
300 300

  
301 301
/* async block I/O */
302 302
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
b/include/block/block_int.h
69 69
    const char *format_name;
70 70
    int instance_size;
71 71

  
72
    /* if not defined external snapshots are allowed
73
     * future block filters will query their children to build the response
72
    /* this table of boolean contains authorizations for the block operations */
73
    bool authorizations[BS_AUTHORIZATION_COUNT];
74
    /* for snapshots complex block filter like Quorum can implement the
75
     * following recursive callback instead of BS_IS_A_FILTER.
76
     * It's purpose is to recurse on the filter children while calling
77
     * bdrv_recurse_is_first_non_filter on them.
78
     * For a sample implementation look in the future Quorum block filter.
74 79
     */
75
    ExtSnapshotPerm (*bdrv_check_ext_snapshot)(BlockDriverState *bs);
80
    bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs,
81
                                             BlockDriverState *candidate);
76 82

  
77 83
    int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
78 84
    int (*bdrv_probe_device)(const char *filename);

Also available in: Unified diff