Example #1
0
int
lwt_lookup_string (int *size, char *knl_ptr,
                   char *user_ptr, int user_size)
{
        int   maxsize = 128;

        /* knl_ptr was retrieved from an LWT snapshot and the caller wants to
         * turn it into a string.  NB we can crash with an access violation
         * trying to determine the string length, so we're trusting our
         * caller... */

        if (!cfs_capable(CFS_CAP_SYS_ADMIN))
                return (-EPERM);

        if (user_size > 0 && 
            maxsize > user_size)
                maxsize = user_size;

        *size = strnlen (knl_ptr, maxsize - 1) + 1;

        if (user_ptr != NULL) {
                if (user_size < 4)
                        return (-EINVAL);

                if (cfs_copy_to_user (user_ptr, knl_ptr, *size))
                        return (-EFAULT);

                /* Did I truncate the string?  */
                if (knl_ptr[*size - 1] != 0)
                        cfs_copy_to_user (user_ptr + *size - 4, "...", 4);
        }

        return (0);
}
Example #2
0
int LL_PROC_PROTO(proc_alloc_fail_rate)
{
        int rc          = 0;
        DECLARE_LL_PROC_PPOS_DECL;

        if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
                *lenp = 0;
                return 0;
        }
        if (write) {
                rc = lprocfs_write_frac_helper(buffer, *lenp,
                                               (unsigned int*)table->data,
                                               OBD_ALLOC_FAIL_MULT);
        } else {
                char buf[21];
                int  len;

                len = lprocfs_read_frac_helper(buf, 21,
                                               *(unsigned int*)table->data,
                                               OBD_ALLOC_FAIL_MULT);
                if (len > *lenp)
                        len = *lenp;
                buf[len] = '\0';
                if (cfs_copy_to_user(buffer, buf, len))
                        return -EFAULT;
                *lenp = len;
        }
        *ppos += *lenp;
        return rc;
}
Example #3
0
int LL_PROC_PROTO(proc_max_dirty_pages_in_mb)
{
        int rc = 0;
        DECLARE_LL_PROC_PPOS_DECL;

        if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
                *lenp = 0;
                return 0;
        }
        if (write) {
                rc = lprocfs_write_frac_helper(buffer, *lenp,
                                               (unsigned int*)table->data,
                                               1 << (20 - CFS_PAGE_SHIFT));
                /* Don't allow them to let dirty pages exceed 90% of system
                 * memory and set a hard minimum of 4MB. */
                if (obd_max_dirty_pages > ((cfs_num_physpages / 10) * 9)) {
                        CERROR("Refusing to set max dirty pages to %u, which "
                               "is more than 90%% of available RAM; setting "
                               "to %lu\n", obd_max_dirty_pages,
                               ((cfs_num_physpages / 10) * 9));
                        obd_max_dirty_pages = ((cfs_num_physpages / 10) * 9);
                } else if (obd_max_dirty_pages < 4 << (20 - CFS_PAGE_SHIFT)) {
                        obd_max_dirty_pages = 4 << (20 - CFS_PAGE_SHIFT);
                }
        } else {
                char buf[21];
                int len;

                len = lprocfs_read_frac_helper(buf, sizeof(buf),
                                               *(unsigned int*)table->data,
                                               1 << (20 - CFS_PAGE_SHIFT));
                if (len > *lenp)
                        len = *lenp;
                buf[len] = '\0';
                if (cfs_copy_to_user(buffer, buf, len))
                        return -EFAULT;
                *lenp = len;
        }
        *ppos += *lenp;
        return rc;
}
Example #4
0
int
lwt_snapshot (cfs_cycles_t *now, int *ncpu, int *total_size,
              void *user_ptr, int user_size)
{
        const int    events_per_page = CFS_PAGE_SIZE / sizeof(lwt_event_t);
        const int    bytes_per_page = events_per_page * sizeof(lwt_event_t);
        lwt_page_t  *p;
        int          i;
        int          j;

        if (!cfs_capable(CFS_CAP_SYS_ADMIN))
                return (-EPERM);

        *ncpu = cfs_num_online_cpus();
        *total_size = cfs_num_online_cpus() * lwt_pages_per_cpu *
                bytes_per_page;
        *now = get_cycles();

        if (user_ptr == NULL)
                return (0);

        for (i = 0; i < cfs_num_online_cpus(); i++) {
                p = lwt_cpus[i].lwtc_current_page;

                if (p == NULL)
                        return (-ENODATA);

                for (j = 0; j < lwt_pages_per_cpu; j++) {
                        if (cfs_copy_to_user(user_ptr, p->lwtp_events,
                                             bytes_per_page))
                                return (-EFAULT);

                        user_ptr = ((char *)user_ptr) + bytes_per_page;
                        p = cfs_list_entry(p->lwtp_list.next,
                                           lwt_page_t, lwtp_list);
                }
        }

        return (0);
}
Example #5
0
int LL_PROC_PROTO(proc_pages_max)
{
        char buf[22];
        int len;
        DECLARE_LL_PROC_PPOS_DECL;

        if (!*lenp || (*ppos && !write)) {
                *lenp = 0;
                return 0;
        }
        if (write)
                return -EINVAL;

        len = snprintf(buf, sizeof(buf), LPU64"\n", obd_pages_max());
        if (len > *lenp)
                len = *lenp;
        buf[len] = '\0';
        if (cfs_copy_to_user(buffer, buf, len))
                return -EFAULT;
        *lenp = len;
        *ppos += *lenp;
        return 0;
}
Example #6
0
/* Retrieve object striping information.
 *
 * @lump is a pointer to an in-core struct with lmm_ost_count indicating
 * the maximum number of OST indices which will fit in the user buffer.
 * lmm_magic must be LOV_USER_MAGIC.
 */
int lov_getstripe(struct obd_export *exp, struct lov_stripe_md *lsm,
                  struct lov_user_md *lump)
{
    /*
     * XXX huge struct allocated on stack.
     */
    /* we use lov_user_md_v3 because it is larger than lov_user_md_v1 */
    struct lov_user_md_v3 lum;
    struct lov_mds_md *lmmk = NULL;
    int rc, lmm_size;
    int lum_size;
    mm_segment_t seg;
    ENTRY;

    if (!lsm)
        RETURN(-ENODATA);

    /*
     * "Switch to kernel segment" to allow copying from kernel space by
     * copy_{to,from}_user().
     */
    seg = get_fs();
    set_fs(KERNEL_DS);

    /* we only need the header part from user space to get lmm_magic and
     * lmm_stripe_count, (the header part is common to v1 and v3) */
    lum_size = sizeof(struct lov_user_md_v1);
    if (cfs_copy_from_user(&lum, lump, lum_size))
        GOTO(out_set, rc = -EFAULT);
    else if ((lum.lmm_magic != LOV_USER_MAGIC) &&
             (lum.lmm_magic != LOV_USER_MAGIC_V3))
        GOTO(out_set, rc = -EINVAL);

    if (lum.lmm_stripe_count &&
            (lum.lmm_stripe_count < lsm->lsm_stripe_count)) {
        /* Return right size of stripe to user */
        lum.lmm_stripe_count = lsm->lsm_stripe_count;
        rc = cfs_copy_to_user(lump, &lum, lum_size);
        GOTO(out_set, rc = -EOVERFLOW);
    }
    rc = lov_packmd(exp, &lmmk, lsm);
    if (rc < 0)
        GOTO(out_set, rc);
    lmm_size = rc;
    rc = 0;

    /* FIXME: Bug 1185 - copy fields properly when structs change */
    /* struct lov_user_md_v3 and struct lov_mds_md_v3 must be the same */
    CLASSERT(sizeof(lum) == sizeof(struct lov_mds_md_v3));
    CLASSERT(sizeof lum.lmm_objects[0] == sizeof lmmk->lmm_objects[0]);

    if ((cpu_to_le32(LOV_MAGIC) != LOV_MAGIC) &&
            ((lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_V1)) ||
             (lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_V3)))) {
        lustre_swab_lov_mds_md(lmmk);
        lustre_swab_lov_user_md_objects(
            (struct lov_user_ost_data*)lmmk->lmm_objects,
            lmmk->lmm_stripe_count);
    }
    if (lum.lmm_magic == LOV_USER_MAGIC) {
        /* User request for v1, we need skip lmm_pool_name */
        if (lmmk->lmm_magic == LOV_MAGIC_V3) {
            memmove((char*)(&lmmk->lmm_stripe_count) +
                    sizeof(lmmk->lmm_stripe_count),
                    ((struct lov_mds_md_v3*)lmmk)->lmm_objects,
                    lmmk->lmm_stripe_count *
                    sizeof(struct lov_ost_data_v1));
            lmm_size -= LOV_MAXPOOLNAME;
        }
    } else {
        /* if v3 we just have to update the lum_size */
        lum_size = sizeof(struct lov_user_md_v3);
    }

    /* User wasn't expecting this many OST entries */
    if (lum.lmm_stripe_count == 0)
        lmm_size = lum_size;
    else if (lum.lmm_stripe_count < lmmk->lmm_stripe_count)
        GOTO(out_set, rc = -EOVERFLOW);
    /*
     * Have a difference between lov_mds_md & lov_user_md.
     * So we have to re-order the data before copy to user.
     */
    lum.lmm_stripe_count = lmmk->lmm_stripe_count;
    ((struct lov_user_md*)lmmk)->lmm_stripe_offset = 0;
    ((struct lov_user_md*)lmmk)->lmm_stripe_count = lum.lmm_stripe_count;
    if (cfs_copy_to_user(lump, lmmk, lmm_size))
        rc = -EFAULT;

    obd_free_diskmd(exp, &lmmk);
out_set:
    set_fs(seg);
    RETURN(rc);
}
Example #7
0
int
lstcon_ioctl_entry(unsigned int cmd, struct libcfs_ioctl_data *data)
{
        char   *buf;
        int     opc = data->ioc_u32[0];
        int     rc;

        if (cmd != IOC_LIBCFS_LNETST)
                return -EINVAL;

        if (data->ioc_plen1 > CFS_PAGE_SIZE)
                return -EINVAL;

        LIBCFS_ALLOC(buf, data->ioc_plen1);
        if (buf == NULL)
                return -ENOMEM;

        /* copy in parameter */
        if (cfs_copy_from_user(buf, data->ioc_pbuf1, data->ioc_plen1)) {
                LIBCFS_FREE(buf, data->ioc_plen1);
                return -EFAULT;
        }

        cfs_mutex_down(&console_session.ses_mutex);

        console_session.ses_laststamp = cfs_time_current_sec();

        if (console_session.ses_shutdown) {
                rc = -ESHUTDOWN;
                goto out;
        }

        if (console_session.ses_expired)
                lstcon_session_end();

        if (opc != LSTIO_SESSION_NEW &&
            console_session.ses_state == LST_SESSION_NONE) {
                CDEBUG(D_NET, "LST no active session\n");
                rc = -ESRCH;
                goto out;
        }

        memset(&console_session.ses_trans_stat, 0, sizeof(lstcon_trans_stat_t));

        switch (opc) {
                case LSTIO_SESSION_NEW:
                        rc = lst_session_new_ioctl((lstio_session_new_args_t *)buf);
                        break;
                case LSTIO_SESSION_END:
                        rc = lst_session_end_ioctl((lstio_session_end_args_t *)buf);
                        break;
                case LSTIO_SESSION_INFO:
                        rc = lst_session_info_ioctl((lstio_session_info_args_t *)buf);
                        break;
                case LSTIO_DEBUG:
                        rc = lst_debug_ioctl((lstio_debug_args_t *)buf);
                        break;
                case LSTIO_GROUP_ADD:
                        rc = lst_group_add_ioctl((lstio_group_add_args_t *)buf);
                        break;
                case LSTIO_GROUP_DEL:
                        rc = lst_group_del_ioctl((lstio_group_del_args_t *)buf);
                        break;
                case LSTIO_GROUP_UPDATE:
                        rc = lst_group_update_ioctl((lstio_group_update_args_t *)buf);
                        break;
                case LSTIO_NODES_ADD:
                        rc = lst_nodes_add_ioctl((lstio_group_nodes_args_t *)buf);
                        break;
                case LSTIO_GROUP_LIST:
                        rc = lst_group_list_ioctl((lstio_group_list_args_t *)buf);
                        break;
                case LSTIO_GROUP_INFO:
                        rc = lst_group_info_ioctl((lstio_group_info_args_t *)buf);
                        break;
                case LSTIO_BATCH_ADD:
                        rc = lst_batch_add_ioctl((lstio_batch_add_args_t *)buf);
                        break;
                case LSTIO_BATCH_START:
                        rc = lst_batch_run_ioctl((lstio_batch_run_args_t *)buf);
                        break;
                case LSTIO_BATCH_STOP:
                        rc = lst_batch_stop_ioctl((lstio_batch_stop_args_t *)buf);
                        break;
                case LSTIO_BATCH_QUERY:
                        rc = lst_batch_query_ioctl((lstio_batch_query_args_t *)buf);
                        break;
                case LSTIO_BATCH_LIST:
                        rc = lst_batch_list_ioctl((lstio_batch_list_args_t *)buf);
                        break;
                case LSTIO_BATCH_INFO:
                        rc = lst_batch_info_ioctl((lstio_batch_info_args_t *)buf);
                        break;
                case LSTIO_TEST_ADD:
                        rc = lst_test_add_ioctl((lstio_test_args_t *)buf);
                        break;
                case LSTIO_STAT_QUERY:
                        rc = lst_stat_query_ioctl((lstio_stat_args_t *)buf);
                        break;
                default:
                        rc = -EINVAL;
        }

        if (cfs_copy_to_user(data->ioc_pbuf2, &console_session.ses_trans_stat,
                             sizeof(lstcon_trans_stat_t)))
                rc = -EFAULT;
out:
        cfs_mutex_up(&console_session.ses_mutex);

        LIBCFS_FREE(buf, data->ioc_plen1);

        return rc;
}
Example #8
0
int lst_test_add_ioctl(lstio_test_args_t *args)
{
        char           *name;
        char           *srcgrp = NULL;
        char           *dstgrp = NULL;
        void           *param = NULL;
        int             ret = 0;
        int             rc = -ENOMEM;

        if (args->lstio_tes_resultp == NULL ||
            args->lstio_tes_retp == NULL ||
            args->lstio_tes_bat_name == NULL || /* no specified batch */
            args->lstio_tes_bat_nmlen <= 0 ||
            args->lstio_tes_bat_nmlen > LST_NAME_SIZE ||
            args->lstio_tes_sgrp_name == NULL || /* no source group */
            args->lstio_tes_sgrp_nmlen <= 0 ||
            args->lstio_tes_sgrp_nmlen > LST_NAME_SIZE ||
            args->lstio_tes_dgrp_name == NULL || /* no target group */
            args->lstio_tes_dgrp_nmlen <= 0 ||
            args->lstio_tes_dgrp_nmlen > LST_NAME_SIZE)
                return -EINVAL;

        /* have parameter, check if parameter length is valid */
        if (args->lstio_tes_param != NULL &&
            (args->lstio_tes_param_len <= 0 ||
             args->lstio_tes_param_len > CFS_PAGE_SIZE - sizeof(lstcon_test_t)))
                return -EINVAL;

        LIBCFS_ALLOC(name, args->lstio_tes_bat_nmlen + 1);
        if (name == NULL)
                return rc;

        LIBCFS_ALLOC(srcgrp, args->lstio_tes_sgrp_nmlen + 1);
        if (srcgrp == NULL) 
                goto out;

        LIBCFS_ALLOC(dstgrp, args->lstio_tes_dgrp_nmlen + 1);
        if (srcgrp == NULL) 
                goto out;

        if (args->lstio_tes_param != NULL) {
                LIBCFS_ALLOC(param, args->lstio_tes_param_len);
                if (param == NULL) 
                        goto out;
        }

        rc = -EFAULT;
        if (cfs_copy_from_user(name,
                              args->lstio_tes_bat_name,
                              args->lstio_tes_bat_nmlen) ||
            cfs_copy_from_user(srcgrp,
                              args->lstio_tes_sgrp_name,
                              args->lstio_tes_sgrp_nmlen) ||
            cfs_copy_from_user(dstgrp,
                              args->lstio_tes_dgrp_name,
                              args->lstio_tes_dgrp_nmlen) ||
            cfs_copy_from_user(param, args->lstio_tes_param,
                              args->lstio_tes_param_len))
                goto out;

        rc = lstcon_test_add(name,
                            args->lstio_tes_type,
                            args->lstio_tes_loop,
                            args->lstio_tes_concur,
                            args->lstio_tes_dist, args->lstio_tes_span,
                            srcgrp, dstgrp, param, args->lstio_tes_param_len,
                            &ret, args->lstio_tes_resultp);

        if (ret != 0)
                rc = (cfs_copy_to_user(args->lstio_tes_retp, &ret,
                                       sizeof(ret))) ? -EFAULT : 0;
out:
        if (name != NULL)
                LIBCFS_FREE(name, args->lstio_tes_bat_nmlen + 1);

        if (srcgrp != NULL)
                LIBCFS_FREE(srcgrp, args->lstio_tes_sgrp_nmlen + 1);

        if (dstgrp != NULL)
                LIBCFS_FREE(dstgrp, args->lstio_tes_dgrp_nmlen + 1);

        if (param != NULL)
                LIBCFS_FREE(param, args->lstio_tes_param_len);

        return rc;
}
Example #9
0
int
lst_batch_info_ioctl(lstio_batch_info_args_t *args)
{
        char           *name;
        int             rc;
        int             index;
        int             ndent;

        if (args->lstio_bat_key != console_session.ses_key)
                return -EACCES;

        if (args->lstio_bat_namep == NULL || /* batch name */
            args->lstio_bat_nmlen <= 0 ||
            args->lstio_bat_nmlen > LST_NAME_SIZE)
                return -EINVAL;

        if (args->lstio_bat_entp == NULL && /* output: batch entry */
            args->lstio_bat_dentsp == NULL) /* output: node entry */
                return -EINVAL;

        if (args->lstio_bat_dentsp != NULL) { /* have node entry */
                if (args->lstio_bat_idxp == NULL || /* node index */
                    args->lstio_bat_ndentp == NULL) /* # of node entry */
                        return -EINVAL;

                if (cfs_copy_from_user(&index, args->lstio_bat_idxp,
                                       sizeof(index)) ||
                    cfs_copy_from_user(&ndent, args->lstio_bat_ndentp,
                                       sizeof(ndent)))
                        return -EFAULT;

                if (ndent <= 0 || index < 0)
                        return -EINVAL;
        }

        LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
        if (name == NULL)
                return -ENOMEM;

        if (cfs_copy_from_user(name,
                               args->lstio_bat_namep, args->lstio_bat_nmlen)) {
                LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
                return -EFAULT;
        }

        name[args->lstio_bat_nmlen] = 0;

        rc = lstcon_batch_info(name,
                            args->lstio_bat_entp, args->lstio_bat_server,
                            args->lstio_bat_testidx, &index, &ndent,
                            args->lstio_bat_dentsp);

        LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);

        if (rc != 0)
                return rc;

        if (args->lstio_bat_dentsp != NULL && 
            (cfs_copy_to_user(args->lstio_bat_idxp, &index, sizeof(index)) ||
             cfs_copy_to_user(args->lstio_bat_ndentp, &ndent, sizeof(ndent))))
                rc = -EFAULT;

        return rc;
}
Example #10
0
int gss_do_ctx_init_rpc(__user char *buffer, unsigned long count)
{
        struct obd_import        *imp;
        struct ptlrpc_request    *req;
        struct lgssd_ioctl_param  param;
        struct obd_device        *obd;
        char                      obdname[64];
        long                      lsize;
        int                       rc;

        if (count != sizeof(param)) {
                CERROR("ioctl size %lu, expect %lu, please check lgss_keyring "
                       "version\n", count, (unsigned long) sizeof(param));
                RETURN(-EINVAL);
        }
        if (cfs_copy_from_user(&param, buffer, sizeof(param))) {
                CERROR("failed copy data from lgssd\n");
                RETURN(-EFAULT);
        }

        if (param.version != GSSD_INTERFACE_VERSION) {
                CERROR("gssd interface version %d (expect %d)\n",
                        param.version, GSSD_INTERFACE_VERSION);
                RETURN(-EINVAL);
        }

        /* take name */
        if (strncpy_from_user(obdname, param.uuid, sizeof(obdname)) <= 0) {
                CERROR("Invalid obdname pointer\n");
                RETURN(-EFAULT);
        }

        obd = class_name2obd(obdname);
        if (!obd) {
                CERROR("no such obd %s\n", obdname);
                RETURN(-EINVAL);
        }

        if (unlikely(!obd->obd_set_up)) {
                CERROR("obd %s not setup\n", obdname);
                RETURN(-EINVAL);
        }

        cfs_spin_lock(&obd->obd_dev_lock);
        if (obd->obd_stopping) {
                CERROR("obd %s has stopped\n", obdname);
                cfs_spin_unlock(&obd->obd_dev_lock);
                RETURN(-EINVAL);
        }

        if (strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) &&
            strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME) &&
            strcmp(obd->obd_type->typ_name, LUSTRE_MGC_NAME)) {
                CERROR("obd %s is not a client device\n", obdname);
                cfs_spin_unlock(&obd->obd_dev_lock);
                RETURN(-EINVAL);
        }
        cfs_spin_unlock(&obd->obd_dev_lock);

        cfs_down_read(&obd->u.cli.cl_sem);
        if (obd->u.cli.cl_import == NULL) {
                CERROR("obd %s: import has gone\n", obd->obd_name);
                cfs_up_read(&obd->u.cli.cl_sem);
                RETURN(-EINVAL);
        }
        imp = class_import_get(obd->u.cli.cl_import);
        cfs_up_read(&obd->u.cli.cl_sem);

        if (imp->imp_deactive) {
                CERROR("import has been deactivated\n");
                class_import_put(imp);
                RETURN(-EINVAL);
        }

        req = ptlrpc_request_alloc_pack(imp, &RQF_SEC_CTX, LUSTRE_OBD_VERSION,
                                        SEC_CTX_INIT);
        if (req == NULL) {
                param.status = -ENOMEM;
                goto out_copy;
        }

        if (req->rq_cli_ctx->cc_sec->ps_id != param.secid) {
                CWARN("original secid %d, now has changed to %d, "
                      "cancel this negotiation\n", param.secid,
                      req->rq_cli_ctx->cc_sec->ps_id);
                param.status = -EINVAL;
                goto out_copy;
        }

        /* get token */
        rc = ctx_init_pack_request(imp, req,
                                   param.lustre_svc,
                                   param.uid, param.gid,
                                   param.send_token_size,
                                   param.send_token);
        if (rc) {
                param.status = rc;
                goto out_copy;
        }

        ptlrpc_request_set_replen(req);

        rc = ptlrpc_queue_wait(req);
        if (rc) {
                /* If any _real_ denial be made, we expect server return
                 * -EACCES reply or return success but indicate gss error
                 * inside reply messsage. All other errors are treated as
                 * timeout, caller might try the negotiation repeatedly,
                 * leave recovery decisions to general ptlrpc layer.
                 *
                 * FIXME maybe some other error code shouldn't be treated
                 * as timeout. */
                param.status = rc;
                if (rc != -EACCES)
                        param.status = -ETIMEDOUT;
                goto out_copy;
        }

        LASSERT(req->rq_repdata);
        lsize = ctx_init_parse_reply(req->rq_repdata,
                                     ptlrpc_rep_need_swab(req),
                                     param.reply_buf, param.reply_buf_size);
        if (lsize < 0) {
                param.status = (int) lsize;
                goto out_copy;
        }

        param.status = 0;
        param.reply_length = lsize;

out_copy:
        if (cfs_copy_to_user(buffer, &param, sizeof(param)))
                rc = -EFAULT;
        else
                rc = 0;

        class_import_put(imp);
        ptlrpc_req_finished(req);
        RETURN(rc);
}
Example #11
0
static
int ctx_init_parse_reply(struct lustre_msg *msg, int swabbed,
                         char __user *outbuf, long outlen)
{
        struct gss_rep_header   *ghdr;
        __u32                    obj_len, round_len;
        __u32                    status, effective = 0;

        if (msg->lm_bufcount != 3) {
                CERROR("unexpected bufcount %u\n", msg->lm_bufcount);
                return -EPROTO;
        }

        ghdr = (struct gss_rep_header *) gss_swab_header(msg, 0, swabbed);
        if (ghdr == NULL) {
                CERROR("unable to extract gss reply header\n");
                return -EPROTO;
        }

        if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
                CERROR("invalid gss version %u\n", ghdr->gh_version);
                return -EPROTO;
        }

        if (outlen < (4 + 2) * 4 + cfs_size_round4(ghdr->gh_handle.len) +
                     cfs_size_round4(msg->lm_buflens[2])) {
                CERROR("output buffer size %ld too small\n", outlen);
                return -EFAULT;
        }

        status = 0;
        effective = 0;

        if (cfs_copy_to_user(outbuf, &status, 4))
                return -EFAULT;
        outbuf += 4;
        if (cfs_copy_to_user(outbuf, &ghdr->gh_major, 4))
                return -EFAULT;
        outbuf += 4;
        if (cfs_copy_to_user(outbuf, &ghdr->gh_minor, 4))
                return -EFAULT;
        outbuf += 4;
        if (cfs_copy_to_user(outbuf, &ghdr->gh_seqwin, 4))
                return -EFAULT;
        outbuf += 4;
        effective += 4 * 4;

        /* handle */
        obj_len = ghdr->gh_handle.len;
        round_len = (obj_len + 3) & ~ 3;
        if (cfs_copy_to_user(outbuf, &obj_len, 4))
                return -EFAULT;
        outbuf += 4;
        if (cfs_copy_to_user(outbuf, (char *) ghdr->gh_handle.data, round_len))
                return -EFAULT;
        outbuf += round_len;
        effective += 4 + round_len;

        /* out token */
        obj_len = msg->lm_buflens[2];
        round_len = (obj_len + 3) & ~ 3;
        if (cfs_copy_to_user(outbuf, &obj_len, 4))
                return -EFAULT;
        outbuf += 4;
        if (cfs_copy_to_user(outbuf, lustre_msg_buf(msg, 2, 0), round_len))
                return -EFAULT;
        outbuf += round_len;
        effective += 4 + round_len;

        return effective;
}