linux/drivers/staging/lustre/lustre/llite/xattr.c
<<
>>
Prefs
   1/*
   2 * GPL HEADER START
   3 *
   4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 only,
   8 * as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License version 2 for more details (a copy is included
  14 * in the LICENSE file that accompanied this code).
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * version 2 along with this program; If not, see
  18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
  19 *
  20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  21 * CA 95054 USA or visit www.sun.com if you need additional information or
  22 * have any questions.
  23 *
  24 * GPL HEADER END
  25 */
  26/*
  27 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  28 * Use is subject to license terms.
  29 *
  30 * Copyright (c) 2011, 2012, Intel Corporation.
  31 */
  32/*
  33 * This file is part of Lustre, http://www.lustre.org/
  34 * Lustre is a trademark of Sun Microsystems, Inc.
  35 */
  36
  37#include <linux/fs.h>
  38#include <linux/sched.h>
  39#include <linux/mm.h>
  40#include <linux/selinux.h>
  41
  42#define DEBUG_SUBSYSTEM S_LLITE
  43
  44#include <obd_support.h>
  45#include <lustre_lite.h>
  46#include <lustre_dlm.h>
  47#include <lustre_ver.h>
  48#include <lustre_eacl.h>
  49
  50#include "llite_internal.h"
  51
  52#define XATTR_USER_T        (1)
  53#define XATTR_TRUSTED_T  (2)
  54#define XATTR_SECURITY_T        (3)
  55#define XATTR_ACL_ACCESS_T      (4)
  56#define XATTR_ACL_DEFAULT_T     (5)
  57#define XATTR_LUSTRE_T    (6)
  58#define XATTR_OTHER_T      (7)
  59
  60static
  61int get_xattr_type(const char *name)
  62{
  63        if (!strcmp(name, POSIX_ACL_XATTR_ACCESS))
  64                return XATTR_ACL_ACCESS_T;
  65
  66        if (!strcmp(name, POSIX_ACL_XATTR_DEFAULT))
  67                return XATTR_ACL_DEFAULT_T;
  68
  69        if (!strncmp(name, XATTR_USER_PREFIX,
  70                     sizeof(XATTR_USER_PREFIX) - 1))
  71                return XATTR_USER_T;
  72
  73        if (!strncmp(name, XATTR_TRUSTED_PREFIX,
  74                     sizeof(XATTR_TRUSTED_PREFIX) - 1))
  75                return XATTR_TRUSTED_T;
  76
  77        if (!strncmp(name, XATTR_SECURITY_PREFIX,
  78                     sizeof(XATTR_SECURITY_PREFIX) - 1))
  79                return XATTR_SECURITY_T;
  80
  81        if (!strncmp(name, XATTR_LUSTRE_PREFIX,
  82                     sizeof(XATTR_LUSTRE_PREFIX) - 1))
  83                return XATTR_LUSTRE_T;
  84
  85        return XATTR_OTHER_T;
  86}
  87
  88static
  89int xattr_type_filter(struct ll_sb_info *sbi, int xattr_type)
  90{
  91        if ((xattr_type == XATTR_ACL_ACCESS_T ||
  92             xattr_type == XATTR_ACL_DEFAULT_T) &&
  93           !(sbi->ll_flags & LL_SBI_ACL))
  94                return -EOPNOTSUPP;
  95
  96        if (xattr_type == XATTR_USER_T && !(sbi->ll_flags & LL_SBI_USER_XATTR))
  97                return -EOPNOTSUPP;
  98        if (xattr_type == XATTR_TRUSTED_T && !cfs_capable(CFS_CAP_SYS_ADMIN))
  99                return -EPERM;
 100        if (xattr_type == XATTR_OTHER_T)
 101                return -EOPNOTSUPP;
 102
 103        return 0;
 104}
 105
 106static
 107int ll_setxattr_common(struct inode *inode, const char *name,
 108                       const void *value, size_t size,
 109                       int flags, __u64 valid)
 110{
 111        struct ll_sb_info *sbi = ll_i2sbi(inode);
 112        struct ptlrpc_request *req;
 113        int xattr_type, rc;
 114        struct obd_capa *oc;
 115#ifdef CONFIG_FS_POSIX_ACL
 116        posix_acl_xattr_header *new_value = NULL;
 117        struct rmtacl_ctl_entry *rce = NULL;
 118        ext_acl_xattr_header *acl = NULL;
 119#endif
 120        const char *pv = value;
 121
 122        xattr_type = get_xattr_type(name);
 123        rc = xattr_type_filter(sbi, xattr_type);
 124        if (rc)
 125                return rc;
 126
 127        /* b10667: ignore lustre special xattr for now */
 128        if ((xattr_type == XATTR_TRUSTED_T && strcmp(name, "trusted.lov") == 0) ||
 129            (xattr_type == XATTR_LUSTRE_T && strcmp(name, "lustre.lov") == 0))
 130                return 0;
 131
 132        /* b15587: ignore security.capability xattr for now */
 133        if ((xattr_type == XATTR_SECURITY_T &&
 134            strcmp(name, "security.capability") == 0))
 135                return 0;
 136
 137        /* LU-549:  Disable security.selinux when selinux is disabled */
 138        if (xattr_type == XATTR_SECURITY_T && !selinux_is_enabled() &&
 139            strcmp(name, "security.selinux") == 0)
 140                return -EOPNOTSUPP;
 141
 142#ifdef CONFIG_FS_POSIX_ACL
 143        if (sbi->ll_flags & LL_SBI_RMT_CLIENT &&
 144            (xattr_type == XATTR_ACL_ACCESS_T ||
 145            xattr_type == XATTR_ACL_DEFAULT_T)) {
 146                rce = rct_search(&sbi->ll_rct, current_pid());
 147                if (rce == NULL ||
 148                    (rce->rce_ops != RMT_LSETFACL &&
 149                    rce->rce_ops != RMT_RSETFACL))
 150                        return -EOPNOTSUPP;
 151
 152                if (rce->rce_ops == RMT_LSETFACL) {
 153                        struct eacl_entry *ee;
 154
 155                        ee = et_search_del(&sbi->ll_et, current_pid(),
 156                                           ll_inode2fid(inode), xattr_type);
 157                        LASSERT(ee != NULL);
 158                        if (valid & OBD_MD_FLXATTR) {
 159                                acl = lustre_acl_xattr_merge2ext(
 160                                                (posix_acl_xattr_header *)value,
 161                                                size, ee->ee_acl);
 162                                if (IS_ERR(acl)) {
 163                                        ee_free(ee);
 164                                        return PTR_ERR(acl);
 165                                }
 166                                size =  CFS_ACL_XATTR_SIZE(\
 167                                                le32_to_cpu(acl->a_count), \
 168                                                ext_acl_xattr);
 169                                pv = (const char *)acl;
 170                        }
 171                        ee_free(ee);
 172                } else if (rce->rce_ops == RMT_RSETFACL) {
 173                        size = lustre_posix_acl_xattr_filter(
 174                                                (posix_acl_xattr_header *)value,
 175                                                size, &new_value);
 176                        if (unlikely(size < 0))
 177                                return size;
 178
 179                        pv = (const char *)new_value;
 180                } else
 181                        return -EOPNOTSUPP;
 182
 183                valid |= rce_ops2valid(rce->rce_ops);
 184        }
 185#endif
 186        oc = ll_mdscapa_get(inode);
 187        rc = md_setxattr(sbi->ll_md_exp, ll_inode2fid(inode), oc,
 188                         valid, name, pv, size, 0, flags, ll_i2suppgid(inode),
 189                         &req);
 190        capa_put(oc);
 191#ifdef CONFIG_FS_POSIX_ACL
 192        if (new_value != NULL)
 193                lustre_posix_acl_xattr_free(new_value, size);
 194        if (acl != NULL)
 195                lustre_ext_acl_xattr_free(acl);
 196#endif
 197        if (rc) {
 198                if (rc == -EOPNOTSUPP && xattr_type == XATTR_USER_T) {
 199                        LCONSOLE_INFO("Disabling user_xattr feature because "
 200                                      "it is not supported on the server\n");
 201                        sbi->ll_flags &= ~LL_SBI_USER_XATTR;
 202                }
 203                return rc;
 204        }
 205
 206        ptlrpc_req_finished(req);
 207        return 0;
 208}
 209
 210int ll_setxattr(struct dentry *dentry, const char *name,
 211                const void *value, size_t size, int flags)
 212{
 213        struct inode *inode = dentry->d_inode;
 214
 215        LASSERT(inode);
 216        LASSERT(name);
 217
 218        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), xattr %s\n",
 219               inode->i_ino, inode->i_generation, inode, name);
 220
 221        ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_SETXATTR, 1);
 222
 223        if ((strncmp(name, XATTR_TRUSTED_PREFIX,
 224                     sizeof(XATTR_TRUSTED_PREFIX) - 1) == 0 &&
 225             strcmp(name + sizeof(XATTR_TRUSTED_PREFIX) - 1, "lov") == 0) ||
 226            (strncmp(name, XATTR_LUSTRE_PREFIX,
 227                     sizeof(XATTR_LUSTRE_PREFIX) - 1) == 0 &&
 228             strcmp(name + sizeof(XATTR_LUSTRE_PREFIX) - 1, "lov") == 0)) {
 229                struct lov_user_md *lump = (struct lov_user_md *)value;
 230                int rc = 0;
 231
 232                /* Attributes that are saved via getxattr will always have
 233                 * the stripe_offset as 0.  Instead, the MDS should be
 234                 * allowed to pick the starting OST index.   b=17846 */
 235                if (lump != NULL && lump->lmm_stripe_offset == 0)
 236                        lump->lmm_stripe_offset = -1;
 237
 238                if (lump != NULL && S_ISREG(inode->i_mode)) {
 239                        struct file f;
 240                        int flags = FMODE_WRITE;
 241                        int lum_size = (lump->lmm_magic == LOV_USER_MAGIC_V1) ?
 242                                sizeof(*lump) : sizeof(struct lov_user_md_v3);
 243
 244                        f.f_dentry = dentry;
 245                        rc = ll_lov_setstripe_ea_info(inode, &f, flags, lump,
 246                                                      lum_size);
 247                        /* b10667: rc always be 0 here for now */
 248                        rc = 0;
 249                } else if (S_ISDIR(inode->i_mode)) {
 250                        rc = ll_dir_setstripe(inode, lump, 0);
 251                }
 252
 253                return rc;
 254
 255        } else if (strcmp(name, XATTR_NAME_LMA) == 0 ||
 256                   strcmp(name, XATTR_NAME_LINK) == 0)
 257                return 0;
 258
 259        return ll_setxattr_common(inode, name, value, size, flags,
 260                                  OBD_MD_FLXATTR);
 261}
 262
 263int ll_removexattr(struct dentry *dentry, const char *name)
 264{
 265        struct inode *inode = dentry->d_inode;
 266
 267        LASSERT(inode);
 268        LASSERT(name);
 269
 270        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), xattr %s\n",
 271               inode->i_ino, inode->i_generation, inode, name);
 272
 273        ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_REMOVEXATTR, 1);
 274        return ll_setxattr_common(inode, name, NULL, 0, 0,
 275                                  OBD_MD_FLXATTRRM);
 276}
 277
 278static
 279int ll_getxattr_common(struct inode *inode, const char *name,
 280                       void *buffer, size_t size, __u64 valid)
 281{
 282        struct ll_sb_info *sbi = ll_i2sbi(inode);
 283        struct ptlrpc_request *req = NULL;
 284        struct mdt_body *body;
 285        int xattr_type, rc;
 286        void *xdata;
 287        struct obd_capa *oc;
 288        struct rmtacl_ctl_entry *rce = NULL;
 289
 290        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p)\n",
 291               inode->i_ino, inode->i_generation, inode);
 292
 293        /* listxattr have slightly different behavior from of ext3:
 294         * without 'user_xattr' ext3 will list all xattr names but
 295         * filtered out "^user..*"; we list them all for simplicity.
 296         */
 297        if (!name) {
 298                xattr_type = XATTR_OTHER_T;
 299                goto do_getxattr;
 300        }
 301
 302        xattr_type = get_xattr_type(name);
 303        rc = xattr_type_filter(sbi, xattr_type);
 304        if (rc)
 305                return rc;
 306
 307        /* b15587: ignore security.capability xattr for now */
 308        if ((xattr_type == XATTR_SECURITY_T &&
 309            strcmp(name, "security.capability") == 0))
 310                return -ENODATA;
 311
 312        /* LU-549:  Disable security.selinux when selinux is disabled */
 313        if (xattr_type == XATTR_SECURITY_T && !selinux_is_enabled() &&
 314            strcmp(name, "security.selinux") == 0)
 315                return -EOPNOTSUPP;
 316
 317#ifdef CONFIG_FS_POSIX_ACL
 318        if (sbi->ll_flags & LL_SBI_RMT_CLIENT &&
 319            (xattr_type == XATTR_ACL_ACCESS_T ||
 320            xattr_type == XATTR_ACL_DEFAULT_T)) {
 321                rce = rct_search(&sbi->ll_rct, current_pid());
 322                if (rce == NULL ||
 323                    (rce->rce_ops != RMT_LSETFACL &&
 324                    rce->rce_ops != RMT_LGETFACL &&
 325                    rce->rce_ops != RMT_RSETFACL &&
 326                    rce->rce_ops != RMT_RGETFACL))
 327                        return -EOPNOTSUPP;
 328        }
 329
 330        /* posix acl is under protection of LOOKUP lock. when calling to this,
 331         * we just have path resolution to the target inode, so we have great
 332         * chance that cached ACL is uptodate.
 333         */
 334        if (xattr_type == XATTR_ACL_ACCESS_T &&
 335            !(sbi->ll_flags & LL_SBI_RMT_CLIENT)) {
 336                struct ll_inode_info *lli = ll_i2info(inode);
 337                struct posix_acl *acl;
 338
 339                spin_lock(&lli->lli_lock);
 340                acl = posix_acl_dup(lli->lli_posix_acl);
 341                spin_unlock(&lli->lli_lock);
 342
 343                if (!acl)
 344                        return -ENODATA;
 345
 346                rc = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
 347                posix_acl_release(acl);
 348                return rc;
 349        }
 350        if (xattr_type == XATTR_ACL_DEFAULT_T && !S_ISDIR(inode->i_mode))
 351                return -ENODATA;
 352#endif
 353
 354do_getxattr:
 355        oc = ll_mdscapa_get(inode);
 356        rc = md_getxattr(sbi->ll_md_exp, ll_inode2fid(inode), oc,
 357                         valid | (rce ? rce_ops2valid(rce->rce_ops) : 0),
 358                         name, NULL, 0, size, 0, &req);
 359        capa_put(oc);
 360        if (rc) {
 361                if (rc == -EOPNOTSUPP && xattr_type == XATTR_USER_T) {
 362                        LCONSOLE_INFO("Disabling user_xattr feature because "
 363                                      "it is not supported on the server\n");
 364                        sbi->ll_flags &= ~LL_SBI_USER_XATTR;
 365                }
 366                return rc;
 367        }
 368
 369        body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
 370        LASSERT(body);
 371
 372        /* only detect the xattr size */
 373        if (size == 0)
 374                GOTO(out, rc = body->eadatasize);
 375
 376        if (size < body->eadatasize) {
 377                CERROR("server bug: replied size %u > %u\n",
 378                       body->eadatasize, (int)size);
 379                GOTO(out, rc = -ERANGE);
 380        }
 381
 382        if (body->eadatasize == 0)
 383                GOTO(out, rc = -ENODATA);
 384
 385        /* do not need swab xattr data */
 386        xdata = req_capsule_server_sized_get(&req->rq_pill, &RMF_EADATA,
 387                                             body->eadatasize);
 388        if (!xdata)
 389                GOTO(out, rc = -EFAULT);
 390
 391#ifdef CONFIG_FS_POSIX_ACL
 392        if (body->eadatasize >= 0 && rce && rce->rce_ops == RMT_LSETFACL) {
 393                ext_acl_xattr_header *acl;
 394
 395                acl = lustre_posix_acl_xattr_2ext((posix_acl_xattr_header *)xdata,
 396                                                  body->eadatasize);
 397                if (IS_ERR(acl))
 398                        GOTO(out, rc = PTR_ERR(acl));
 399
 400                rc = ee_add(&sbi->ll_et, current_pid(), ll_inode2fid(inode),
 401                            xattr_type, acl);
 402                if (unlikely(rc < 0)) {
 403                        lustre_ext_acl_xattr_free(acl);
 404                        GOTO(out, rc);
 405                }
 406        }
 407#endif
 408
 409        if (body->eadatasize == 0) {
 410                rc = -ENODATA;
 411        } else {
 412                LASSERT(buffer);
 413                memcpy(buffer, xdata, body->eadatasize);
 414                rc = body->eadatasize;
 415        }
 416out:
 417        ptlrpc_req_finished(req);
 418        return rc;
 419}
 420
 421ssize_t ll_getxattr(struct dentry *dentry, const char *name,
 422                    void *buffer, size_t size)
 423{
 424        struct inode *inode = dentry->d_inode;
 425
 426        LASSERT(inode);
 427        LASSERT(name);
 428
 429        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), xattr %s\n",
 430               inode->i_ino, inode->i_generation, inode, name);
 431
 432        ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_GETXATTR, 1);
 433
 434        if ((strncmp(name, XATTR_TRUSTED_PREFIX,
 435                     sizeof(XATTR_TRUSTED_PREFIX) - 1) == 0 &&
 436             strcmp(name + sizeof(XATTR_TRUSTED_PREFIX) - 1, "lov") == 0) ||
 437            (strncmp(name, XATTR_LUSTRE_PREFIX,
 438                     sizeof(XATTR_LUSTRE_PREFIX) - 1) == 0 &&
 439             strcmp(name + sizeof(XATTR_LUSTRE_PREFIX) - 1, "lov") == 0)) {
 440                struct lov_stripe_md *lsm;
 441                struct lov_user_md *lump;
 442                struct lov_mds_md *lmm = NULL;
 443                struct ptlrpc_request *request = NULL;
 444                int rc = 0, lmmsize = 0;
 445
 446                if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
 447                        return -ENODATA;
 448
 449                if (size == 0 && S_ISDIR(inode->i_mode)) {
 450                        /* XXX directory EA is fix for now, optimize to save
 451                         * RPC transfer */
 452                        GOTO(out, rc = sizeof(struct lov_user_md));
 453                }
 454
 455                lsm = ccc_inode_lsm_get(inode);
 456                if (lsm == NULL) {
 457                        if (S_ISDIR(inode->i_mode)) {
 458                                rc = ll_dir_getstripe(inode, &lmm,
 459                                                      &lmmsize, &request);
 460                        } else {
 461                                rc = -ENODATA;
 462                        }
 463                } else {
 464                        /* LSM is present already after lookup/getattr call.
 465                         * we need to grab layout lock once it is implemented */
 466                        rc = obd_packmd(ll_i2dtexp(inode), &lmm, lsm);
 467                        lmmsize = rc;
 468                }
 469                ccc_inode_lsm_put(inode, lsm);
 470
 471                if (rc < 0)
 472                       GOTO(out, rc);
 473
 474                if (size == 0) {
 475                        /* used to call ll_get_max_mdsize() forward to get
 476                         * the maximum buffer size, while some apps (such as
 477                         * rsync 3.0.x) care much about the exact xattr value
 478                         * size */
 479                        rc = lmmsize;
 480                        GOTO(out, rc);
 481                }
 482
 483                if (size < lmmsize) {
 484                        CERROR("server bug: replied size %d > %d for %s (%s)\n",
 485                               lmmsize, (int)size, dentry->d_name.name, name);
 486                        GOTO(out, rc = -ERANGE);
 487                }
 488
 489                lump = (struct lov_user_md *)buffer;
 490                memcpy(lump, lmm, lmmsize);
 491                /* do not return layout gen for getxattr otherwise it would
 492                 * confuse tar --xattr by recognizing layout gen as stripe
 493                 * offset when the file is restored. See LU-2809. */
 494                lump->lmm_layout_gen = 0;
 495
 496                rc = lmmsize;
 497out:
 498                if (request)
 499                        ptlrpc_req_finished(request);
 500                else if (lmm)
 501                        obd_free_diskmd(ll_i2dtexp(inode), &lmm);
 502                return(rc);
 503        }
 504
 505        return ll_getxattr_common(inode, name, buffer, size, OBD_MD_FLXATTR);
 506}
 507
 508ssize_t ll_listxattr(struct dentry *dentry, char *buffer, size_t size)
 509{
 510        struct inode *inode = dentry->d_inode;
 511        int rc = 0, rc2 = 0;
 512        struct lov_mds_md *lmm = NULL;
 513        struct ptlrpc_request *request = NULL;
 514        int lmmsize;
 515
 516        LASSERT(inode);
 517
 518        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p)\n",
 519               inode->i_ino, inode->i_generation, inode);
 520
 521        ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_LISTXATTR, 1);
 522
 523        rc = ll_getxattr_common(inode, NULL, buffer, size, OBD_MD_FLXATTRLS);
 524        if (rc < 0)
 525                GOTO(out, rc);
 526
 527        if (buffer != NULL) {
 528                struct ll_sb_info *sbi = ll_i2sbi(inode);
 529                char *xattr_name = buffer;
 530                int xlen, rem = rc;
 531
 532                while (rem > 0) {
 533                        xlen = strnlen(xattr_name, rem - 1) + 1;
 534                        rem -= xlen;
 535                        if (xattr_type_filter(sbi,
 536                                        get_xattr_type(xattr_name)) == 0) {
 537                                /* skip OK xattr type
 538                                 * leave it in buffer
 539                                 */
 540                                xattr_name += xlen;
 541                                continue;
 542                        }
 543                        /* move up remaining xattrs in buffer
 544                         * removing the xattr that is not OK
 545                         */
 546                        memmove(xattr_name, xattr_name + xlen, rem);
 547                        rc -= xlen;
 548                }
 549        }
 550        if (S_ISREG(inode->i_mode)) {
 551                if (!ll_i2info(inode)->lli_has_smd)
 552                        rc2 = -1;
 553        } else if (S_ISDIR(inode->i_mode)) {
 554                rc2 = ll_dir_getstripe(inode, &lmm, &lmmsize, &request);
 555        }
 556
 557        if (rc2 < 0) {
 558                GOTO(out, rc2 = 0);
 559        } else if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) {
 560                const int prefix_len = sizeof(XATTR_LUSTRE_PREFIX) - 1;
 561                const size_t name_len   = sizeof("lov") - 1;
 562                const size_t total_len  = prefix_len + name_len + 1;
 563
 564                if (((rc + total_len) > size) && (buffer != NULL)) {
 565                        ptlrpc_req_finished(request);
 566                        return -ERANGE;
 567                }
 568
 569                if (buffer != NULL) {
 570                        buffer += rc;
 571                        memcpy(buffer, XATTR_LUSTRE_PREFIX, prefix_len);
 572                        memcpy(buffer + prefix_len, "lov", name_len);
 573                        buffer[prefix_len + name_len] = '\0';
 574                }
 575                rc2 = total_len;
 576        }
 577out:
 578        ptlrpc_req_finished(request);
 579        rc = rc + rc2;
 580
 581        return rc;
 582}
 583