|
|
7711c0 |
From ac4ba4bb4b2118dab2e7f0b7c7e3daec3698d060 Mon Sep 17 00:00:00 2001
|
|
|
7711c0 |
From: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Date: Wed, 3 Apr 2019 18:18:51 +0200
|
|
|
7711c0 |
Subject: [PATCH 146/163] block/dirty-bitmaps: add block_dirty_bitmap_check
|
|
|
7711c0 |
function
|
|
|
7711c0 |
|
|
|
7711c0 |
RH-Author: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Message-id: <20190403181857.9693-16-jsnow@redhat.com>
|
|
|
7711c0 |
Patchwork-id: 85429
|
|
|
7711c0 |
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 15/21] block/dirty-bitmaps: add block_dirty_bitmap_check function
|
|
|
7711c0 |
Bugzilla: 1677073
|
|
|
7711c0 |
RH-Acked-by: Max Reitz <mreitz@redhat.com>
|
|
|
7711c0 |
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
7711c0 |
RH-Acked-by: Sergio Lopez Pascual <slp@redhat.com>
|
|
|
7711c0 |
|
|
|
7711c0 |
Instead of checking against busy, inconsistent, or read only directly,
|
|
|
7711c0 |
use a check function with permissions bits that let us streamline the
|
|
|
7711c0 |
checks without reproducing them in many places.
|
|
|
7711c0 |
|
|
|
7711c0 |
Included in this patch are permissions changes that simply add the
|
|
|
7711c0 |
inconsistent check to existing permissions call spots, without
|
|
|
7711c0 |
addressing existing bugs.
|
|
|
7711c0 |
|
|
|
7711c0 |
In general, this means that busy+readonly checks become BDRV_BITMAP_DEFAULT,
|
|
|
7711c0 |
which checks against all three conditions. busy-only checks become
|
|
|
7711c0 |
BDRV_BITMAP_ALLOW_RO.
|
|
|
7711c0 |
|
|
|
7711c0 |
Notably, remove allows inconsistent bitmaps, so it doesn't follow the pattern.
|
|
|
7711c0 |
|
|
|
7711c0 |
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Reviewed-by: Eric Blake <eblake@redhat.com>
|
|
|
7711c0 |
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
|
|
7711c0 |
Message-id: 20190301191545.8728-4-jsnow@redhat.com
|
|
|
7711c0 |
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
(cherry picked from commit 3ae96d66840f72ef54902d012dbdf87ef4e9fe0c)
|
|
|
7711c0 |
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
7711c0 |
---
|
|
|
7711c0 |
block/dirty-bitmap.c | 42 +++++++++++++++++++++++++-----------
|
|
|
7711c0 |
blockdev.c | 49 ++++++++----------------------------------
|
|
|
7711c0 |
include/block/dirty-bitmap.h | 13 ++++++++++-
|
|
|
7711c0 |
migration/block-dirty-bitmap.c | 13 ++++-------
|
|
|
7711c0 |
nbd/server.c | 3 +--
|
|
|
7711c0 |
5 files changed, 56 insertions(+), 64 deletions(-)
|
|
|
7711c0 |
|
|
|
7711c0 |
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
|
|
|
7711c0 |
index 4a2349d..6170f3a 100644
|
|
|
7711c0 |
--- a/block/dirty-bitmap.c
|
|
|
7711c0 |
+++ b/block/dirty-bitmap.c
|
|
|
7711c0 |
@@ -174,7 +174,7 @@ bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap)
|
|
|
7711c0 |
return bitmap->successor;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
-bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap)
|
|
|
7711c0 |
+static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap)
|
|
|
7711c0 |
{
|
|
|
7711c0 |
return bitmap->busy;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
@@ -236,6 +236,33 @@ static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap)
|
|
|
7711c0 |
!bitmap->successor->disabled);
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
+int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
|
|
7711c0 |
+ Error **errp)
|
|
|
7711c0 |
+{
|
|
|
7711c0 |
+ if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) {
|
|
|
7711c0 |
+ error_setg(errp, "Bitmap '%s' is currently in use by another"
|
|
|
7711c0 |
+ " operation and cannot be used", bitmap->name);
|
|
|
7711c0 |
+ return -1;
|
|
|
7711c0 |
+ }
|
|
|
7711c0 |
+
|
|
|
7711c0 |
+ if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) {
|
|
|
7711c0 |
+ error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
|
|
7711c0 |
+ bitmap->name);
|
|
|
7711c0 |
+ return -1;
|
|
|
7711c0 |
+ }
|
|
|
7711c0 |
+
|
|
|
7711c0 |
+ if ((flags & BDRV_BITMAP_INCONSISTENT) &&
|
|
|
7711c0 |
+ bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
|
|
7711c0 |
+ error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used",
|
|
|
7711c0 |
+ bitmap->name);
|
|
|
7711c0 |
+ error_append_hint(errp, "Try block-dirty-bitmap-remove to delete"
|
|
|
7711c0 |
+ " this bitmap from disk");
|
|
|
7711c0 |
+ return -1;
|
|
|
7711c0 |
+ }
|
|
|
7711c0 |
+
|
|
|
7711c0 |
+ return 0;
|
|
|
7711c0 |
+}
|
|
|
7711c0 |
+
|
|
|
7711c0 |
/**
|
|
|
7711c0 |
* Create a successor bitmap destined to replace this bitmap after an operation.
|
|
|
7711c0 |
* Requires that the bitmap is not marked busy and has no successor.
|
|
|
7711c0 |
@@ -248,9 +275,7 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
|
|
7711c0 |
uint64_t granularity;
|
|
|
7711c0 |
BdrvDirtyBitmap *child;
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bitmap)) {
|
|
|
7711c0 |
- error_setg(errp, "Cannot create a successor for a bitmap that is "
|
|
|
7711c0 |
- "in-use by an operation");
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
|
|
|
7711c0 |
return -1;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
if (bdrv_dirty_bitmap_has_successor(bitmap)) {
|
|
|
7711c0 |
@@ -796,17 +821,10 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
|
|
7711c0 |
|
|
|
7711c0 |
qemu_mutex_lock(dest->mutex);
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(dest)) {
|
|
|
7711c0 |
- error_setg(errp, "Bitmap '%s' is currently in use by another"
|
|
|
7711c0 |
- " operation and cannot be modified", dest->name);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
|
|
|
7711c0 |
goto out;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_readonly(dest)) {
|
|
|
7711c0 |
- error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
|
|
7711c0 |
- dest->name);
|
|
|
7711c0 |
- goto out;
|
|
|
7711c0 |
- }
|
|
|
7711c0 |
|
|
|
7711c0 |
if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
|
|
|
7711c0 |
error_setg(errp, "Bitmaps are incompatible and can't be merged");
|
|
|
7711c0 |
diff --git a/blockdev.c b/blockdev.c
|
|
|
7711c0 |
index a9e2e1d..860cea6 100644
|
|
|
7711c0 |
--- a/blockdev.c
|
|
|
7711c0 |
+++ b/blockdev.c
|
|
|
7711c0 |
@@ -2160,11 +2160,7 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(state->bitmap)) {
|
|
|
7711c0 |
- error_setg(errp, "Cannot modify a bitmap in use by another operation");
|
|
|
7711c0 |
- return;
|
|
|
7711c0 |
- } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
|
|
|
7711c0 |
- error_setg(errp, "Cannot clear a readonly bitmap");
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) {
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
@@ -2209,10 +2205,7 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(state->bitmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation"
|
|
|
7711c0 |
- " and cannot be enabled", action->name);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
@@ -2250,10 +2243,7 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(state->bitmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation"
|
|
|
7711c0 |
- " and cannot be disabled", action->name);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
@@ -3044,10 +3034,7 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bitmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation and"
|
|
|
7711c0 |
- " cannot be removed", name);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
@@ -3083,13 +3070,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bitmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation"
|
|
|
7711c0 |
- " and cannot be cleared", name);
|
|
|
7711c0 |
- return;
|
|
|
7711c0 |
- } else if (bdrv_dirty_bitmap_readonly(bitmap)) {
|
|
|
7711c0 |
- error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
@@ -3107,10 +3088,7 @@ void qmp_block_dirty_bitmap_enable(const char *node, const char *name,
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bitmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation"
|
|
|
7711c0 |
- " and cannot be enabled", name);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
@@ -3128,10 +3106,7 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name,
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bitmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation"
|
|
|
7711c0 |
- " and cannot be disabled", name);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
|
|
7711c0 |
return;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
@@ -3709,10 +3684,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
|
|
7711c0 |
bdrv_unref(target_bs);
|
|
|
7711c0 |
goto out;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation"
|
|
|
7711c0 |
- " and cannot be used for backup", backup->bitmap);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
|
|
7711c0 |
goto out;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
}
|
|
|
7711c0 |
@@ -3816,10 +3788,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
|
|
7711c0 |
error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
|
|
|
7711c0 |
goto out;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bmap)) {
|
|
|
7711c0 |
- error_setg(errp,
|
|
|
7711c0 |
- "Bitmap '%s' is currently in use by another operation"
|
|
|
7711c0 |
- " and cannot be used for backup", backup->bitmap);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
|
|
7711c0 |
goto out;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
}
|
|
|
7711c0 |
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
|
|
|
7711c0 |
index bd1b647..2a78243 100644
|
|
|
7711c0 |
--- a/include/block/dirty-bitmap.h
|
|
|
7711c0 |
+++ b/include/block/dirty-bitmap.h
|
|
|
7711c0 |
@@ -5,6 +5,16 @@
|
|
|
7711c0 |
#include "qapi/qapi-types-block-core.h"
|
|
|
7711c0 |
#include "qemu/hbitmap.h"
|
|
|
7711c0 |
|
|
|
7711c0 |
+typedef enum BitmapCheckFlags {
|
|
|
7711c0 |
+ BDRV_BITMAP_BUSY = 1,
|
|
|
7711c0 |
+ BDRV_BITMAP_RO = 2,
|
|
|
7711c0 |
+ BDRV_BITMAP_INCONSISTENT = 4,
|
|
|
7711c0 |
+} BitmapCheckFlags;
|
|
|
7711c0 |
+
|
|
|
7711c0 |
+#define BDRV_BITMAP_DEFAULT (BDRV_BITMAP_BUSY | BDRV_BITMAP_RO | \
|
|
|
7711c0 |
+ BDRV_BITMAP_INCONSISTENT)
|
|
|
7711c0 |
+#define BDRV_BITMAP_ALLOW_RO (BDRV_BITMAP_BUSY | BDRV_BITMAP_INCONSISTENT)
|
|
|
7711c0 |
+
|
|
|
7711c0 |
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
|
|
7711c0 |
uint32_t granularity,
|
|
|
7711c0 |
const char *name,
|
|
|
7711c0 |
@@ -24,6 +34,8 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
|
|
7711c0 |
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap);
|
|
|
7711c0 |
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs,
|
|
|
7711c0 |
const char *name);
|
|
|
7711c0 |
+int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
|
|
7711c0 |
+ Error **errp);
|
|
|
7711c0 |
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap);
|
|
|
7711c0 |
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs);
|
|
|
7711c0 |
void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
|
|
7711c0 |
@@ -93,7 +105,6 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs);
|
|
|
7711c0 |
bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap);
|
|
|
7711c0 |
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap);
|
|
|
7711c0 |
bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap);
|
|
|
7711c0 |
-bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap);
|
|
|
7711c0 |
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
|
|
|
7711c0 |
BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
|
|
|
7711c0 |
BdrvDirtyBitmap *bitmap);
|
|
|
7711c0 |
diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c
|
|
|
7711c0 |
index 62f2806..06ab58d 100644
|
|
|
7711c0 |
--- a/migration/block-dirty-bitmap.c
|
|
|
7711c0 |
+++ b/migration/block-dirty-bitmap.c
|
|
|
7711c0 |
@@ -274,6 +274,7 @@ static int init_dirty_bitmap_migration(void)
|
|
|
7711c0 |
BdrvDirtyBitmap *bitmap;
|
|
|
7711c0 |
DirtyBitmapMigBitmapState *dbms;
|
|
|
7711c0 |
BdrvNextIterator it;
|
|
|
7711c0 |
+ Error *local_err = NULL;
|
|
|
7711c0 |
|
|
|
7711c0 |
dirty_bitmap_mig_state.bulk_completed = false;
|
|
|
7711c0 |
dirty_bitmap_mig_state.prev_bs = NULL;
|
|
|
7711c0 |
@@ -301,15 +302,9 @@ static int init_dirty_bitmap_migration(void)
|
|
|
7711c0 |
goto fail;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bitmap)) {
|
|
|
7711c0 |
- error_report("Can't migrate a bitmap that is in use by another operation: '%s'",
|
|
|
7711c0 |
- bdrv_dirty_bitmap_name(bitmap));
|
|
|
7711c0 |
- goto fail;
|
|
|
7711c0 |
- }
|
|
|
7711c0 |
-
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_readonly(bitmap)) {
|
|
|
7711c0 |
- error_report("Can't migrate read-only dirty bitmap: '%s",
|
|
|
7711c0 |
- bdrv_dirty_bitmap_name(bitmap));
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT,
|
|
|
7711c0 |
+ &local_err)) {
|
|
|
7711c0 |
+ error_report_err(local_err);
|
|
|
7711c0 |
goto fail;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
diff --git a/nbd/server.c b/nbd/server.c
|
|
|
7711c0 |
index 02773e2..9b87c7f 100644
|
|
|
7711c0 |
--- a/nbd/server.c
|
|
|
7711c0 |
+++ b/nbd/server.c
|
|
|
7711c0 |
@@ -1510,8 +1510,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
|
|
|
7711c0 |
goto fail;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (bdrv_dirty_bitmap_busy(bm)) {
|
|
|
7711c0 |
- error_setg(errp, "Bitmap '%s' is in use", bitmap);
|
|
|
7711c0 |
+ if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) {
|
|
|
7711c0 |
goto fail;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
--
|
|
|
7711c0 |
1.8.3.1
|
|
|
7711c0 |
|