Blame xlators/cluster/afr/src/afr-self-heal-metadata.c

Packit Service e080da
/*
Packit Service e080da
  Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com>
Packit Service e080da
  This file is part of GlusterFS.
Packit Service e080da
Packit Service e080da
  This file is licensed to you under your choice of the GNU Lesser
Packit Service e080da
  General Public License, version 3 or any later version (LGPLv3 or
Packit Service e080da
  later), or the GNU General Public License, version 2 (GPLv2), in all
Packit Service e080da
  cases as published by the Free Software Foundation.
Packit Service e080da
*/
Packit Service e080da
Packit Service e080da
#include "afr.h"
Packit Service e080da
#include "afr-self-heal.h"
Packit Service e080da
#include <glusterfs/byte-order.h>
Packit Service e080da
#include "protocol-common.h"
Packit Service e080da
#include <glusterfs/events.h>
Packit Service e080da
Packit Service e080da
#define AFR_HEAL_ATTR (GF_SET_ATTR_UID | GF_SET_ATTR_GID | GF_SET_ATTR_MODE)
Packit Service e080da
Packit Service e080da
static gf_boolean_t
Packit Service e080da
_afr_ignorable_key_match(dict_t *d, char *k, data_t *val, void *mdata)
Packit Service e080da
{
Packit Service e080da
    return afr_is_xattr_ignorable(k);
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
void
Packit Service e080da
afr_delete_ignorable_xattrs(dict_t *xattr)
Packit Service e080da
{
Packit Service e080da
    dict_foreach_match(xattr, _afr_ignorable_key_match, NULL,
Packit Service e080da
                       dict_remove_foreach_fn, NULL);
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
int
Packit Service e080da
__afr_selfheal_metadata_do(call_frame_t *frame, xlator_t *this, inode_t *inode,
Packit Service e080da
                           int source, unsigned char *healed_sinks,
Packit Service e080da
                           struct afr_reply *locked_replies)
Packit Service e080da
{
Packit Service e080da
    int ret = -1;
Packit Service e080da
    loc_t loc = {
Packit Service e080da
        0,
Packit Service e080da
    };
Packit Service e080da
    dict_t *xattr = NULL;
Packit Service e080da
    dict_t *old_xattr = NULL;
Packit Service e080da
    afr_private_t *priv = NULL;
Packit Service e080da
    int i = 0;
Packit Service e080da
Packit Service e080da
    priv = this->private;
Packit Service e080da
Packit Service e080da
    loc.inode = inode_ref(inode);
Packit Service e080da
    gf_uuid_copy(loc.gfid, inode->gfid);
Packit Service e080da
Packit Service e080da
    gf_msg(this->name, GF_LOG_INFO, 0, AFR_MSG_SELF_HEAL_INFO,
Packit Service e080da
           "performing metadata selfheal on %s", uuid_utoa(inode->gfid));
Packit Service e080da
Packit Service e080da
    ret = syncop_getxattr(priv->children[source], &loc, &xattr, NULL, NULL,
Packit Service e080da
                          NULL);
Packit Service e080da
    if (ret < 0) {
Packit Service e080da
        ret = -EIO;
Packit Service e080da
        goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    afr_delete_ignorable_xattrs(xattr);
Packit Service e080da
Packit Service e080da
    for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
        if (old_xattr) {
Packit Service e080da
            dict_unref(old_xattr);
Packit Service e080da
            old_xattr = NULL;
Packit Service e080da
        }
Packit Service e080da
Packit Service e080da
        if (!healed_sinks[i])
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        ret = syncop_setattr(priv->children[i], &loc,
Packit Service e080da
                             &locked_replies[source].poststat, AFR_HEAL_ATTR,
Packit Service e080da
                             NULL, NULL, NULL, NULL);
Packit Service e080da
        if (ret)
Packit Service e080da
            healed_sinks[i] = 0;
Packit Service e080da
Packit Service e080da
        ret = syncop_getxattr(priv->children[i], &loc, &old_xattr, 0, NULL,
Packit Service e080da
                              NULL);
Packit Service e080da
        if (old_xattr) {
Packit Service e080da
            afr_delete_ignorable_xattrs(old_xattr);
Packit Service e080da
            ret = syncop_removexattr(priv->children[i], &loc, "", old_xattr,
Packit Service e080da
                                     NULL);
Packit Service e080da
            if (ret)
Packit Service e080da
                healed_sinks[i] = 0;
Packit Service e080da
        }
Packit Service e080da
Packit Service e080da
        ret = syncop_setxattr(priv->children[i], &loc, xattr, 0, NULL, NULL);
Packit Service e080da
        if (ret)
Packit Service e080da
            healed_sinks[i] = 0;
Packit Service e080da
    }
Packit Service e080da
    ret = 0;
Packit Service e080da
Packit Service e080da
out:
Packit Service e080da
    loc_wipe(&loc;;
Packit Service e080da
    if (xattr)
Packit Service e080da
        dict_unref(xattr);
Packit Service e080da
    if (old_xattr)
Packit Service e080da
        dict_unref(old_xattr);
Packit Service e080da
Packit Service e080da
    return ret;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
static uint64_t
Packit Service e080da
mtime_ns(struct iatt *ia)
Packit Service e080da
{
Packit Service e080da
    uint64_t ret;
Packit Service e080da
Packit Service e080da
    ret = (((uint64_t)(ia->ia_mtime)) * 1000000000) +
Packit Service e080da
          (uint64_t)(ia->ia_mtime_nsec);
Packit Service e080da
Packit Service e080da
    return ret;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
/*
Packit Service e080da
 * When directory content is modified, [mc]time is updated. On
Packit Service e080da
 * Linux, the filesystem does it, while at least on NetBSD, the
Packit Service e080da
 * kernel file-system independent code does it. This means that
Packit Service e080da
 * when entries are added while bricks are down, the kernel sends
Packit Service e080da
 * a SETATTR [mc]time which will cause metadata split brain for
Packit Service e080da
 * the directory. In this case, clear the split brain by finding
Packit Service e080da
 * the source with the most recent modification date.
Packit Service e080da
 */
Packit Service e080da
static int
Packit Service e080da
afr_dirtime_splitbrain_source(call_frame_t *frame, xlator_t *this,
Packit Service e080da
                              struct afr_reply *replies,
Packit Service e080da
                              unsigned char *locked_on)
Packit Service e080da
{
Packit Service e080da
    afr_private_t *priv = NULL;
Packit Service e080da
    int source = -1;
Packit Service e080da
    struct iatt source_ia;
Packit Service e080da
    struct iatt child_ia;
Packit Service e080da
    uint64_t mtime = 0;
Packit Service e080da
    int i;
Packit Service e080da
    int ret = -1;
Packit Service e080da
Packit Service e080da
    priv = this->private;
Packit Service e080da
Packit Service e080da
    for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
        if (!locked_on[i])
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        if (!replies[i].valid)
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        if (replies[i].op_ret != 0)
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        if (mtime_ns(&replies[i].poststat) <= mtime)
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        mtime = mtime_ns(&replies[i].poststat);
Packit Service e080da
        source = i;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    if (source == -1)
Packit Service e080da
        goto out;
Packit Service e080da
Packit Service e080da
    source_ia = replies[source].poststat;
Packit Service e080da
    if (source_ia.ia_type != IA_IFDIR)
Packit Service e080da
        goto out;
Packit Service e080da
Packit Service e080da
    for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
        if (i == source)
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        if (!replies[i].valid)
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        if (replies[i].op_ret != 0)
Packit Service e080da
            continue;
Packit Service e080da
Packit Service e080da
        child_ia = replies[i].poststat;
Packit Service e080da
Packit Service e080da
        if (!IA_EQUAL(source_ia, child_ia, gfid) ||
Packit Service e080da
            !IA_EQUAL(source_ia, child_ia, type) ||
Packit Service e080da
            !IA_EQUAL(source_ia, child_ia, prot) ||
Packit Service e080da
            !IA_EQUAL(source_ia, child_ia, uid) ||
Packit Service e080da
            !IA_EQUAL(source_ia, child_ia, gid) ||
Packit Service e080da
            !afr_xattrs_are_equal(replies[source].xdata, replies[i].xdata))
Packit Service e080da
            goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    /*
Packit Service e080da
     * Metadata split brain is just about [amc]time
Packit Service e080da
     * We return our source.
Packit Service e080da
     */
Packit Service e080da
    ret = source;
Packit Service e080da
out:
Packit Service e080da
    return ret;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
/*
Packit Service e080da
 * Look for mismatching uid/gid or mode or user xattrs even if
Packit Service e080da
 * AFR xattrs don't say so, and pick one arbitrarily as winner. */
Packit Service e080da
Packit Service e080da
static int
Packit Service e080da
__afr_selfheal_metadata_finalize_source(call_frame_t *frame, xlator_t *this,
Packit Service e080da
                                        inode_t *inode, unsigned char *sources,
Packit Service e080da
                                        unsigned char *sinks,
Packit Service e080da
                                        unsigned char *healed_sinks,
Packit Service e080da
                                        unsigned char *undid_pending,
Packit Service e080da
                                        unsigned char *locked_on,
Packit Service e080da
                                        struct afr_reply *replies)
Packit Service e080da
{
Packit Service e080da
    int i = 0;
Packit Service e080da
    afr_private_t *priv = NULL;
Packit Service e080da
    struct iatt srcstat = {
Packit Service e080da
        0,
Packit Service e080da
    };
Packit Service e080da
    int source = -1;
Packit Service e080da
    int sources_count = 0;
Packit Service e080da
Packit Service e080da
    priv = this->private;
Packit Service e080da
Packit Service e080da
    sources_count = AFR_COUNT(sources, priv->child_count);
Packit Service e080da
Packit Service e080da
    if ((AFR_CMP(locked_on, healed_sinks, priv->child_count) == 0) ||
Packit Service e080da
        !sources_count) {
Packit Service e080da
        source = afr_mark_split_brain_source_sinks(
Packit Service e080da
            frame, this, inode, sources, sinks, healed_sinks, locked_on,
Packit Service e080da
            replies, AFR_METADATA_TRANSACTION);
Packit Service e080da
        if (source >= 0) {
Packit Service e080da
            _afr_fav_child_reset_sink_xattrs(
Packit Service e080da
                frame, this, inode, source, healed_sinks, undid_pending,
Packit Service e080da
                AFR_METADATA_TRANSACTION, locked_on, replies);
Packit Service e080da
            goto out;
Packit Service e080da
        }
Packit Service e080da
Packit Service e080da
        /* If this is a directory mtime/ctime only split brain
Packit Service e080da
           use the most recent */
Packit Service e080da
        source = afr_dirtime_splitbrain_source(frame, this, replies, locked_on);
Packit Service e080da
        if (source != -1) {
Packit Service e080da
            gf_msg(this->name, GF_LOG_INFO, 0, AFR_MSG_SPLIT_BRAIN,
Packit Service e080da
                   "clear time "
Packit Service e080da
                   "split brain on %s",
Packit Service e080da
                   uuid_utoa(replies[source].poststat.ia_gfid));
Packit Service e080da
            sources[source] = 1;
Packit Service e080da
            healed_sinks[source] = 0;
Packit Service e080da
            goto out;
Packit Service e080da
        }
Packit Service e080da
Packit Service e080da
        if (!priv->metadata_splitbrain_forced_heal) {
Packit Service e080da
            gf_event(EVENT_AFR_SPLIT_BRAIN,
Packit Service e080da
                     "subvol=%s;"
Packit Service e080da
                     "type=metadata;file=%s",
Packit Service 35f350
                     this->name, uuid_utoa(inode->gfid));
Packit Service e080da
            return -EIO;
Packit Service e080da
        }
Packit Service e080da
Packit Service e080da
        /* Metadata split brain, select one subvol
Packit Service e080da
           arbitrarily */
Packit Service e080da
        for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
            if (locked_on[i] && healed_sinks[i]) {
Packit Service e080da
                sources[i] = 1;
Packit Service e080da
                healed_sinks[i] = 0;
Packit Service e080da
                break;
Packit Service e080da
            }
Packit Service e080da
        }
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    /* No split brain at this point. If we were called from
Packit Service e080da
     * afr_heal_splitbrain_file(), abort.*/
Packit Service e080da
    if (afr_dict_contains_heal_op(frame))
Packit Service e080da
        return -EIO;
Packit Service e080da
Packit Service e080da
    source = afr_choose_source_by_policy(priv, sources,
Packit Service e080da
                                         AFR_METADATA_TRANSACTION);
Packit Service e080da
    srcstat = replies[source].poststat;
Packit Service e080da
Packit Service e080da
    for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
        if (!sources[i] || i == source)
Packit Service e080da
            continue;
Packit Service e080da
        if (!IA_EQUAL(srcstat, replies[i].poststat, type) ||
Packit Service e080da
            !IA_EQUAL(srcstat, replies[i].poststat, uid) ||
Packit Service e080da
            !IA_EQUAL(srcstat, replies[i].poststat, gid) ||
Packit Service e080da
            !IA_EQUAL(srcstat, replies[i].poststat, prot)) {
Packit Service e080da
            gf_msg_debug(this->name, 0,
Packit Service e080da
                         "%s: iatt mismatch "
Packit Service e080da
                         "for source(%d) vs (%d)",
Packit Service e080da
                         uuid_utoa(replies[source].poststat.ia_gfid), source,
Packit Service e080da
                         i);
Packit Service e080da
            sources[i] = 0;
Packit Service e080da
            healed_sinks[i] = 1;
Packit Service e080da
        }
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
        if (!sources[i] || i == source)
Packit Service e080da
            continue;
Packit Service e080da
        if (!afr_xattrs_are_equal(replies[source].xdata, replies[i].xdata)) {
Packit Service e080da
            gf_msg_debug(this->name, 0,
Packit Service e080da
                         "%s: xattr mismatch "
Packit Service e080da
                         "for source(%d) vs (%d)",
Packit Service e080da
                         uuid_utoa(replies[source].poststat.ia_gfid), source,
Packit Service e080da
                         i);
Packit Service e080da
            sources[i] = 0;
Packit Service e080da
            healed_sinks[i] = 1;
Packit Service e080da
        }
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
out:
Packit Service e080da
    afr_mark_active_sinks(this, sources, locked_on, healed_sinks);
Packit Service e080da
    return source;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
int
Packit Service e080da
__afr_selfheal_metadata_prepare(call_frame_t *frame, xlator_t *this,
Packit Service e080da
                                inode_t *inode, unsigned char *locked_on,
Packit Service e080da
                                unsigned char *sources, unsigned char *sinks,
Packit Service e080da
                                unsigned char *healed_sinks,
Packit Service e080da
                                unsigned char *undid_pending,
Packit Service e080da
                                struct afr_reply *replies, unsigned char *pflag)
Packit Service e080da
{
Packit Service e080da
    int ret = -1;
Packit Service e080da
    int source = -1;
Packit Service e080da
    afr_private_t *priv = NULL;
Packit Service e080da
    int i = 0;
Packit Service e080da
    uint64_t *witness = NULL;
Packit Service e080da
Packit Service e080da
    priv = this->private;
Packit Service e080da
Packit Service e080da
    ret = afr_selfheal_unlocked_discover(frame, inode, inode->gfid, replies);
Packit Service e080da
    if (ret)
Packit Service e080da
        return ret;
Packit Service e080da
Packit Service e080da
    witness = alloca0(sizeof(*witness) * priv->child_count);
Packit Service e080da
    ret = afr_selfheal_find_direction(frame, this, replies,
Packit Service e080da
                                      AFR_METADATA_TRANSACTION, locked_on,
Packit Service e080da
                                      sources, sinks, witness, pflag);
Packit Service e080da
    if (ret)
Packit Service e080da
        return ret;
Packit Service e080da
Packit Service e080da
    /* Initialize the healed_sinks[] array optimistically to
Packit Service e080da
       the intersection of to-be-healed (i.e sinks[]) and
Packit Service e080da
       the list of servers which are up (i.e locked_on[]).
Packit Service e080da
Packit Service e080da
       As we encounter failures in the healing process, we
Packit Service e080da
       will unmark the respective servers in the healed_sinks[]
Packit Service e080da
       array.
Packit Service e080da
    */
Packit Service e080da
    AFR_INTERSECT(healed_sinks, sinks, locked_on, priv->child_count);
Packit Service e080da
Packit Service e080da
    /* If any source has witness, pick first
Packit Service e080da
     * witness source and make everybody else sinks */
Packit Service e080da
    for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
        if (sources[i] && witness[i]) {
Packit Service e080da
            source = i;
Packit Service e080da
            break;
Packit Service e080da
        }
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    if (source != -1) {
Packit Service e080da
        for (i = 0; i < priv->child_count; i++) {
Packit Service e080da
            if (i != source && sources[i]) {
Packit Service e080da
                sources[i] = 0;
Packit Service e080da
                healed_sinks[i] = 1;
Packit Service e080da
            }
Packit Service e080da
        }
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    source = __afr_selfheal_metadata_finalize_source(
Packit Service e080da
        frame, this, inode, sources, sinks, healed_sinks, undid_pending,
Packit Service e080da
        locked_on, replies);
Packit Service e080da
Packit Service e080da
    if (source < 0)
Packit Service e080da
        return -EIO;
Packit Service e080da
Packit Service e080da
    return source;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
int
Packit Service e080da
afr_selfheal_metadata(call_frame_t *frame, xlator_t *this, inode_t *inode)
Packit Service e080da
{
Packit Service e080da
    afr_private_t *priv = NULL;
Packit Service e080da
    int ret = -1;
Packit Service e080da
    unsigned char *sources = NULL;
Packit Service e080da
    unsigned char *sinks = NULL;
Packit Service e080da
    unsigned char *data_lock = NULL;
Packit Service e080da
    unsigned char *healed_sinks = NULL;
Packit Service e080da
    unsigned char *undid_pending = NULL;
Packit Service e080da
    struct afr_reply *locked_replies = NULL;
Packit Service e080da
    gf_boolean_t did_sh = _gf_true;
Packit Service e080da
    int source = -1;
Packit Service e080da
Packit Service e080da
    priv = this->private;
Packit Service e080da
Packit Service e080da
    sources = alloca0(priv->child_count);
Packit Service e080da
    sinks = alloca0(priv->child_count);
Packit Service e080da
    healed_sinks = alloca0(priv->child_count);
Packit Service e080da
    undid_pending = alloca0(priv->child_count);
Packit Service e080da
    data_lock = alloca0(priv->child_count);
Packit Service e080da
Packit Service e080da
    locked_replies = alloca0(sizeof(*locked_replies) * priv->child_count);
Packit Service e080da
Packit Service e080da
    ret = afr_selfheal_inodelk(frame, this, inode, this->name, LLONG_MAX - 1, 0,
Packit Service e080da
                               data_lock);
Packit Service e080da
    {
Packit Service 35f350
        if (ret < AFR_SH_MIN_PARTICIPANTS) {
Packit Service e080da
            ret = -ENOTCONN;
Packit Service e080da
            goto unlock;
Packit Service e080da
        }
Packit Service e080da
Packit Service e080da
        ret = __afr_selfheal_metadata_prepare(
Packit Service e080da
            frame, this, inode, data_lock, sources, sinks, healed_sinks,
Packit Service e080da
            undid_pending, locked_replies, NULL);
Packit Service e080da
        if (ret < 0)
Packit Service e080da
            goto unlock;
Packit Service e080da
Packit Service e080da
        source = ret;
Packit Service e080da
Packit Service e080da
        if (AFR_COUNT(healed_sinks, priv->child_count) == 0) {
Packit Service e080da
            did_sh = _gf_false;
Packit Service e080da
            goto unlock;
Packit Service e080da
        }
Packit Service e080da
Packit Service e080da
        ret = __afr_selfheal_metadata_do(frame, this, inode, source,
Packit Service e080da
                                         healed_sinks, locked_replies);
Packit Service e080da
        if (ret)
Packit Service e080da
            goto unlock;
Packit Service e080da
Packit Service e080da
        /* Restore atime/mtime for files that don't need data heal as
Packit Service e080da
         * restoring timestamps happens only as a part of data-heal.
Packit Service e080da
         */
Packit Service e080da
        if (!IA_ISREG(locked_replies[source].poststat.ia_type))
Packit Service e080da
            afr_selfheal_restore_time(frame, this, inode, source, healed_sinks,
Packit Service e080da
                                      locked_replies);
Packit Service e080da
Packit Service e080da
        ret = afr_selfheal_undo_pending(
Packit Service e080da
            frame, this, inode, sources, sinks, healed_sinks, undid_pending,
Packit Service e080da
            AFR_METADATA_TRANSACTION, locked_replies, data_lock);
Packit Service e080da
    }
Packit Service e080da
unlock:
Packit Service e080da
    afr_selfheal_uninodelk(frame, this, inode, this->name, LLONG_MAX - 1, 0,
Packit Service e080da
                           data_lock);
Packit Service e080da
Packit Service e080da
    if (did_sh)
Packit Service e080da
        afr_log_selfheal(inode->gfid, this, ret, "metadata", source, sources,
Packit Service e080da
                         healed_sinks);
Packit Service e080da
    else
Packit Service e080da
        ret = 1;
Packit Service e080da
Packit Service e080da
    if (locked_replies)
Packit Service e080da
        afr_replies_wipe(locked_replies, priv->child_count);
Packit Service e080da
    return ret;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
int
Packit Service e080da
afr_selfheal_metadata_by_stbuf(xlator_t *this, struct iatt *stbuf)
Packit Service e080da
{
Packit Service e080da
    inode_t *inode = NULL;
Packit Service e080da
    inode_t *link_inode = NULL;
Packit Service e080da
    call_frame_t *frame = NULL;
Packit Service e080da
    int ret = 0;
Packit Service e080da
Packit Service e080da
    if (gf_uuid_is_null(stbuf->ia_gfid)) {
Packit Service e080da
        ret = -EINVAL;
Packit Service e080da
        goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    inode = inode_new(this->itable);
Packit Service e080da
    if (!inode) {
Packit Service e080da
        ret = -ENOMEM;
Packit Service e080da
        goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    link_inode = inode_link(inode, NULL, NULL, stbuf);
Packit Service e080da
    if (!link_inode) {
Packit Service e080da
        ret = -ENOMEM;
Packit Service e080da
        goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    frame = afr_frame_create(this, &ret;;
Packit Service e080da
    if (!frame) {
Packit Service e080da
        ret = -ret;
Packit Service e080da
        goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    ret = afr_selfheal_metadata(frame, this, link_inode);
Packit Service e080da
out:
Packit Service e080da
    if (inode)
Packit Service e080da
        inode_unref(inode);
Packit Service e080da
    if (link_inode)
Packit Service e080da
        inode_unref(link_inode);
Packit Service e080da
    if (frame)
Packit Service e080da
        AFR_STACK_DESTROY(frame);
Packit Service e080da
    return ret;
Packit Service e080da
}