GQuark  bd_loop_error_quark (void) {
        return g_quark_from_static_string ("g-bd-loop-error-quark");
}

/**
 * BDLoopInfo:
 * @backing_file: backing file for the give loop device;
 * @offset: offset of the start of the device (in @backing_file);
 * @autoclear: whether the autoclear flag is set or not;
 * @direct_io: whether direct IO is enabled or not;
 * @part_scan: whether the partition scan is enforced or not;
 * @read_only: whether the device is read-only or not;
 */
/**
 * bd_loop_info_free: (skip)
 * @info: (nullable): %BDLoopInfo to free
 *
 * Frees @info.
 */
void  bd_loop_info_free (BDLoopInfo *info) {
        if (info == NULL)
        return;

    g_free (info->backing_file);
    g_free (info);
}

/**
 * bd_loop_info_copy: (skip)
 * @info: (nullable): %BDLoopInfo to copy
 *
 * Creates a new copy of @info.
 */
BDLoopInfo* bd_loop_info_copy (BDLoopInfo *info) {
        if (info == NULL)
        return NULL;

    BDLoopInfo *new_info = g_new0 (BDLoopInfo, 1);

    new_info->backing_file = g_strdup (info->backing_file);
    new_info->offset = info->offset;
    new_info->autoclear = info->autoclear;
    new_info->direct_io = info->direct_io;
    new_info->part_scan = info->part_scan;
    new_info->read_only = info->read_only;

    return new_info;
}

GType  bd_loop_info_get_type () {
        static GType type = 0;

    if (G_UNLIKELY(type == 0)) {
        type = g_boxed_type_register_static("BDLoopInfo",
                                            (GBoxedCopyFunc) bd_loop_info_copy,
                                            (GBoxedFreeFunc) bd_loop_info_free);
    }

    return type;
}

static gboolean  bd_loop_is_tech_avail_stub (BDLoopTech tech G_GNUC_UNUSED, guint64 mode G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_is_tech_avail' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_is_tech_avail' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_loop_is_tech_avail) (BDLoopTech tech, guint64 mode, GError **error) = bd_loop_is_tech_avail_stub;

/**
 * bd_loop_is_tech_avail:
 * @tech: the queried tech
 * @mode: a bit mask of queried modes of operation (#BDLoopTechMode) for @tech
 * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
 *
 * Returns: whether the @tech-@mode combination is available -- supported by the
 *          plugin implementation and having all the runtime dependencies available
 */
gboolean  bd_loop_is_tech_avail (BDLoopTech tech, guint64 mode, GError **error) {
    return _bd_loop_is_tech_avail (tech, mode, error);
}


static BDLoopInfo* bd_loop_info_stub (const gchar *loop G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_info' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_info' called, but not implemented!");
    return NULL;
}

static BDLoopInfo* (*_bd_loop_info) (const gchar *loop, GError **error) = bd_loop_info_stub;

/**
 * bd_loop_info:
 * @loop: name of the loop device to get information about (e.g. "loop0")
 * @error: (out) (optional): place to store error (if any)
 *
 * Returns: (transfer full): information about the @loop device or %NULL in case of error
 *
 * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_QUERY
 */
BDLoopInfo* bd_loop_info (const gchar *loop, GError **error) {
    return _bd_loop_info (loop, error);
}


static gchar* bd_loop_get_loop_name_stub (const gchar *file G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_get_loop_name' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_get_loop_name' called, but not implemented!");
    return NULL;
}

static gchar* (*_bd_loop_get_loop_name) (const gchar *file, GError **error) = bd_loop_get_loop_name_stub;

/**
 * bd_loop_get_loop_name:
 * @file: path of the backing file to get loop name for
 * @error: (out) (optional): place to store error (if any)
 *
 * Returns: (transfer full): name of the loop device associated with the given
 * @file or %NULL if failed to determine
 *
 * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_QUERY
 */
gchar* bd_loop_get_loop_name (const gchar *file, GError **error) {
    return _bd_loop_get_loop_name (file, error);
}


static gboolean  bd_loop_setup_stub (const gchar *file G_GNUC_UNUSED, guint64 offset G_GNUC_UNUSED, guint64 size G_GNUC_UNUSED, gboolean read_only G_GNUC_UNUSED, gboolean part_scan G_GNUC_UNUSED, guint64 sector_size G_GNUC_UNUSED, const gchar **loop_name G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_setup' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_setup' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_loop_setup) (const gchar *file, guint64 offset, guint64 size, gboolean read_only, gboolean part_scan, guint64 sector_size, const gchar **loop_name, GError **error) = bd_loop_setup_stub;

/**
 * bd_loop_setup:
 * @file: file to setup as a loop device
 * @offset: offset of the start of the device (in @file)
 * @size: maximum size of the device (or 0 to leave unspecified)
 * @read_only: whether to setup as read-only (%TRUE) or read-write (%FALSE)
 * @part_scan: whether to enforce partition scan on the newly created device or not
 * @sector_size: logical sector size for the loop device in bytes (or 0 for default)
 * @loop_name: (optional) (out): if not %NULL, it is used to store the name of the loop device
 * @error: (out) (optional): place to store error (if any)
 *
 * Returns: whether the @file was successfully setup as a loop device or not
 *
 * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_CREATE
 */
gboolean  bd_loop_setup (const gchar *file, guint64 offset, guint64 size, gboolean read_only, gboolean part_scan, guint64 sector_size, const gchar **loop_name, GError **error) {
    return _bd_loop_setup (file, offset, size, read_only, part_scan, sector_size, loop_name, error);
}


static gboolean  bd_loop_setup_from_fd_stub (gint fd G_GNUC_UNUSED, guint64 offset G_GNUC_UNUSED, guint64 size G_GNUC_UNUSED, gboolean read_only G_GNUC_UNUSED, gboolean part_scan G_GNUC_UNUSED, guint64 sector_size G_GNUC_UNUSED, const gchar **loop_name G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_setup_from_fd' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_setup_from_fd' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_loop_setup_from_fd) (gint fd, guint64 offset, guint64 size, gboolean read_only, gboolean part_scan, guint64 sector_size, const gchar **loop_name, GError **error) = bd_loop_setup_from_fd_stub;

/**
 * bd_loop_setup_from_fd:
 * @fd: file descriptor for a file to setup as a new loop device
 * @offset: offset of the start of the device (in file given by @fd)
 * @size: maximum size of the device (or 0 to leave unspecified)
 * @read_only: whether to setup as read-only (%TRUE) or read-write (%FALSE)
 * @part_scan: whether to enforce partition scan on the newly created device or not
 * @sector_size: logical sector size for the loop device in bytes (or 0 for default)
 * @loop_name: (optional) (out): if not %NULL, it is used to store the name of the loop device
 * @error: (out) (optional): place to store error (if any)
 *
 * Returns: whether a new loop device was successfully setup for @fd or not
 *
 * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_CREATE
 */
gboolean  bd_loop_setup_from_fd (gint fd, guint64 offset, guint64 size, gboolean read_only, gboolean part_scan, guint64 sector_size, const gchar **loop_name, GError **error) {
    return _bd_loop_setup_from_fd (fd, offset, size, read_only, part_scan, sector_size, loop_name, error);
}


static gboolean  bd_loop_teardown_stub (const gchar *loop G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_teardown' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_teardown' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_loop_teardown) (const gchar *loop, GError **error) = bd_loop_teardown_stub;

/**
 * bd_loop_teardown:
 * @loop: path or name of the loop device to tear down
 * @error: (out) (optional): place to store error (if any)
 *
 * Returns: whether the @loop device was successfully torn down or not
 *
 * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_DESTROY
 */
gboolean  bd_loop_teardown (const gchar *loop, GError **error) {
    return _bd_loop_teardown (loop, error);
}


static gboolean  bd_loop_set_autoclear_stub (const gchar *loop G_GNUC_UNUSED, gboolean autoclear G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_set_autoclear' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_set_autoclear' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_loop_set_autoclear) (const gchar *loop, gboolean autoclear, GError **error) = bd_loop_set_autoclear_stub;

/**
 * bd_loop_set_autoclear:
 * @loop: path or name of the loop device
 * @autoclear: whether to set or unset the autoclear flag
 * @error: (out) (optional): place to store error (if any)
 *
 * Returns: whether the autoclear flag was successfully set on the @loop device or not
 *
 * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_MODIFY
 */
gboolean  bd_loop_set_autoclear (const gchar *loop, gboolean autoclear, GError **error) {
    return _bd_loop_set_autoclear (loop, autoclear, error);
}


static gboolean  bd_loop_set_capacity_stub (const gchar *loop G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_loop_set_capacity' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_loop_set_capacity' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_loop_set_capacity) (const gchar *loop, GError **error) = bd_loop_set_capacity_stub;

/**
 * bd_loop_set_capacity:
 * @loop: path or name of the loop device
 * @error: (out) (optional): place to store error (if any)
 *
 * Force the loop driver to reread the size of the file associated with the
 * specified @loop device.
 *
 * Returns: whether the LOOP_SET_CAPACITY ioctl was successfully issued or not.
 *
 * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_MODIFY
 */
gboolean  bd_loop_set_capacity (const gchar *loop, GError **error) {
    return _bd_loop_set_capacity (loop, error);
}


static gpointer load_loop_from_plugin(const gchar *so_name) {
    void *handle = NULL;
    char *error = NULL;
    gboolean (*init_fn) (void) = NULL;

    handle = dlopen(so_name, RTLD_LAZY | RTLD_NODELETE);
    if (!handle) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load module loop: %s", dlerror());
        return NULL;
    }

    dlerror();
    * (void**) (&init_fn) = dlsym(handle, "bd_loop_init");
    if ((error = dlerror()) != NULL)
        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "failed to load the init() function for loop: %s", error);
    /* coverity[dead_error_condition] */
    if (init_fn && !init_fn()) {
        dlclose(handle);
        return NULL;
    }
    init_fn = NULL;

    dlerror();
    * (void**) (&_bd_loop_is_tech_avail) = dlsym(handle, "bd_loop_is_tech_avail");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_is_tech_avail: %s", error);
        _bd_loop_is_tech_avail = bd_loop_is_tech_avail_stub;
    }

    dlerror();
    * (void**) (&_bd_loop_info) = dlsym(handle, "bd_loop_info");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_info: %s", error);
        _bd_loop_info = bd_loop_info_stub;
    }

    dlerror();
    * (void**) (&_bd_loop_get_loop_name) = dlsym(handle, "bd_loop_get_loop_name");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_get_loop_name: %s", error);
        _bd_loop_get_loop_name = bd_loop_get_loop_name_stub;
    }

    dlerror();
    * (void**) (&_bd_loop_setup) = dlsym(handle, "bd_loop_setup");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_setup: %s", error);
        _bd_loop_setup = bd_loop_setup_stub;
    }

    dlerror();
    * (void**) (&_bd_loop_setup_from_fd) = dlsym(handle, "bd_loop_setup_from_fd");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_setup_from_fd: %s", error);
        _bd_loop_setup_from_fd = bd_loop_setup_from_fd_stub;
    }

    dlerror();
    * (void**) (&_bd_loop_teardown) = dlsym(handle, "bd_loop_teardown");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_teardown: %s", error);
        _bd_loop_teardown = bd_loop_teardown_stub;
    }

    dlerror();
    * (void**) (&_bd_loop_set_autoclear) = dlsym(handle, "bd_loop_set_autoclear");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_set_autoclear: %s", error);
        _bd_loop_set_autoclear = bd_loop_set_autoclear_stub;
    }

    dlerror();
    * (void**) (&_bd_loop_set_capacity) = dlsym(handle, "bd_loop_set_capacity");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_loop_set_capacity: %s", error);
        _bd_loop_set_capacity = bd_loop_set_capacity_stub;
    }

    return handle;
}

static gboolean unload_loop (gpointer handle) {
    char *error = NULL;
    gboolean (*close_fn) (void) = NULL;

    _bd_loop_is_tech_avail = bd_loop_is_tech_avail_stub;
    _bd_loop_info = bd_loop_info_stub;
    _bd_loop_get_loop_name = bd_loop_get_loop_name_stub;
    _bd_loop_setup = bd_loop_setup_stub;
    _bd_loop_setup_from_fd = bd_loop_setup_from_fd_stub;
    _bd_loop_teardown = bd_loop_teardown_stub;
    _bd_loop_set_autoclear = bd_loop_set_autoclear_stub;
    _bd_loop_set_capacity = bd_loop_set_capacity_stub;

    dlerror();
    * (void**) (&close_fn) = dlsym(handle, "bd_loop_close");
    if (((error = dlerror()) != NULL) || !close_fn)
        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "failed to load the close_plugin() function for loop: %s", error);
    /* coverity[dead_error_condition] */
    if (close_fn) {
        close_fn();
    }

    return dlclose(handle) == 0;
}

