Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfs: share xattr name and value buffers when logging xattr updates

While running xfs/297 and generic/642, I noticed a crash in
xfs_attri_item_relog when it tries to copy the attr name to the new
xattri log item. I think what happened here was that we called
->iop_commit on the old attri item (which nulls out the pointers) as
part of a log force at the same time that a chained attr operation was
ongoing. The system was busy enough that at some later point, the defer
ops operation decided it was necessary to relog the attri log item, but
as we've detached the name buffer from the old attri log item, we can't
copy it to the new one, and kaboom.

I think there's a broader refcounting problem with LARP mode -- the
setxattr code can return to userspace before the CIL actually formats
and commits the log item, which results in a UAF bug. Therefore, the
xattr log item needs to be able to retain a reference to the name and
value buffers until the log items have completely cleared the log.
Furthermore, each time we create an intent log item, we allocate new
memory and (re)copy the contents; sharing here would be very useful.

Solve the UAF and the unnecessary memory allocations by having the log
code create a single refcounted buffer to contain the name and value
contents. This buffer can be passed from old to new during a relog
operation, and the logging code can (optionally) attach it to the
xfs_attr_item for reuse when LARP mode is enabled.

This also fixes a problem where the xfs_attri_log_item objects weren't
being freed back to the same cache where they came from.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>

authored by

Darrick J. Wong and committed by
Dave Chinner
4183e4f2 22a68ba7

+222 -133
+8
fs/xfs/libxfs/xfs_attr.h
··· 502 502 { XFS_DAS_NODE_REMOVE_ATTR, "XFS_DAS_NODE_REMOVE_ATTR" }, \ 503 503 { XFS_DAS_DONE, "XFS_DAS_DONE" } 504 504 505 + struct xfs_attri_log_nameval; 506 + 505 507 /* 506 508 * Context used for keeping track of delayed attribute operations 507 509 */ ··· 518 516 struct xfs_da_state *xattri_da_state; 519 517 520 518 struct xfs_da_args *xattri_da_args; 519 + 520 + /* 521 + * Shared buffer containing the attr name and value so that the logging 522 + * code can share large memory buffers between log items. 523 + */ 524 + struct xfs_attri_log_nameval *xattri_nameval; 521 525 522 526 /* 523 527 * Used by xfs_attr_set to hold a leaf buffer across a transaction roll
+47 -12
fs/xfs/libxfs/xfs_defer.c
··· 191 191 [XFS_DEFER_OPS_TYPE_ATTR] = &xfs_attr_defer_type, 192 192 }; 193 193 194 - static bool 194 + /* 195 + * Ensure there's a log intent item associated with this deferred work item if 196 + * the operation must be restarted on crash. Returns 1 if there's a log item; 197 + * 0 if there isn't; or a negative errno. 198 + */ 199 + static int 195 200 xfs_defer_create_intent( 196 201 struct xfs_trans *tp, 197 202 struct xfs_defer_pending *dfp, 198 203 bool sort) 199 204 { 200 205 const struct xfs_defer_op_type *ops = defer_op_types[dfp->dfp_type]; 206 + struct xfs_log_item *lip; 201 207 202 - if (!dfp->dfp_intent) 203 - dfp->dfp_intent = ops->create_intent(tp, &dfp->dfp_work, 204 - dfp->dfp_count, sort); 205 - return dfp->dfp_intent != NULL; 208 + if (dfp->dfp_intent) 209 + return 1; 210 + 211 + lip = ops->create_intent(tp, &dfp->dfp_work, dfp->dfp_count, sort); 212 + if (!lip) 213 + return 0; 214 + if (IS_ERR(lip)) 215 + return PTR_ERR(lip); 216 + 217 + dfp->dfp_intent = lip; 218 + return 1; 206 219 } 207 220 208 221 /* 209 222 * For each pending item in the intake list, log its intent item and the 210 223 * associated extents, then add the entire intake list to the end of 211 224 * the pending list. 225 + * 226 + * Returns 1 if at least one log item was associated with the deferred work; 227 + * 0 if there are no log items; or a negative errno. 212 228 */ 213 - static bool 229 + static int 214 230 xfs_defer_create_intents( 215 231 struct xfs_trans *tp) 216 232 { 217 233 struct xfs_defer_pending *dfp; 218 - bool ret = false; 234 + int ret = 0; 219 235 220 236 list_for_each_entry(dfp, &tp->t_dfops, dfp_list) { 237 + int ret2; 238 + 221 239 trace_xfs_defer_create_intent(tp->t_mountp, dfp); 222 - ret |= xfs_defer_create_intent(tp, dfp, true); 240 + ret2 = xfs_defer_create_intent(tp, dfp, true); 241 + if (ret2 < 0) 242 + return ret2; 243 + ret |= ret2; 223 244 } 224 245 return ret; 225 246 } ··· 478 457 dfp->dfp_count--; 479 458 error = ops->finish_item(tp, dfp->dfp_done, li, &state); 480 459 if (error == -EAGAIN) { 460 + int ret; 461 + 481 462 /* 482 463 * Caller wants a fresh transaction; put the work item 483 464 * back on the list and log a new log intent item to ··· 490 467 dfp->dfp_count++; 491 468 dfp->dfp_done = NULL; 492 469 dfp->dfp_intent = NULL; 493 - xfs_defer_create_intent(tp, dfp, false); 470 + ret = xfs_defer_create_intent(tp, dfp, false); 471 + if (ret < 0) 472 + error = ret; 494 473 } 495 474 496 475 if (error) ··· 539 514 * of time that any one intent item can stick around in memory, 540 515 * pinning the log tail. 541 516 */ 542 - bool has_intents = xfs_defer_create_intents(*tp); 517 + int has_intents = xfs_defer_create_intents(*tp); 543 518 544 519 list_splice_init(&(*tp)->t_dfops, &dop_pending); 545 520 521 + if (has_intents < 0) { 522 + error = has_intents; 523 + goto out_shutdown; 524 + } 546 525 if (has_intents || dfp) { 547 526 error = xfs_defer_trans_roll(tp); 548 527 if (error) ··· 705 676 if (list_empty(&tp->t_dfops)) 706 677 return NULL; 707 678 679 + error = xfs_defer_create_intents(tp); 680 + if (error < 0) 681 + return ERR_PTR(error); 682 + 708 683 /* Create an object to capture the defer ops. */ 709 684 dfc = kmem_zalloc(sizeof(*dfc), KM_NOFS); 710 685 INIT_LIST_HEAD(&dfc->dfc_list); 711 686 INIT_LIST_HEAD(&dfc->dfc_dfops); 712 - 713 - xfs_defer_create_intents(tp); 714 687 715 688 /* Move the dfops chain and transaction state to the capture struct. */ 716 689 list_splice_init(&tp->t_dfops, &dfc->dfc_dfops); ··· 790 759 791 760 /* If we don't capture anything, commit transaction and exit. */ 792 761 dfc = xfs_defer_ops_capture(tp); 762 + if (IS_ERR(dfc)) { 763 + xfs_trans_cancel(tp); 764 + return PTR_ERR(dfc); 765 + } 793 766 if (!dfc) 794 767 return xfs_trans_commit(tp); 795 768
+151 -117
fs/xfs/xfs_attr_item.c
··· 42 42 return container_of(lip, struct xfs_attri_log_item, attri_item); 43 43 } 44 44 45 + /* 46 + * Shared xattr name/value buffers for logged extended attribute operations 47 + * 48 + * When logging updates to extended attributes, we can create quite a few 49 + * attribute log intent items for a single xattr update. To avoid cycling the 50 + * memory allocator and memcpy overhead, the name (and value, for setxattr) 51 + * are kept in a refcounted object that is shared across all related log items 52 + * and the upper-level deferred work state structure. The shared buffer has 53 + * a control structure, followed by the name, and then the value. 54 + */ 55 + 56 + static inline struct xfs_attri_log_nameval * 57 + xfs_attri_log_nameval_get( 58 + struct xfs_attri_log_nameval *nv) 59 + { 60 + if (!refcount_inc_not_zero(&nv->refcount)) 61 + return NULL; 62 + return nv; 63 + } 64 + 65 + static inline void 66 + xfs_attri_log_nameval_put( 67 + struct xfs_attri_log_nameval *nv) 68 + { 69 + if (!nv) 70 + return; 71 + if (refcount_dec_and_test(&nv->refcount)) 72 + kvfree(nv); 73 + } 74 + 75 + static inline struct xfs_attri_log_nameval * 76 + xfs_attri_log_nameval_alloc( 77 + const void *name, 78 + unsigned int name_len, 79 + const void *value, 80 + unsigned int value_len) 81 + { 82 + struct xfs_attri_log_nameval *nv; 83 + 84 + /* 85 + * This could be over 64kB in length, so we have to use kvmalloc() for 86 + * this. But kvmalloc() utterly sucks, so we use our own version. 87 + */ 88 + nv = xlog_kvmalloc(sizeof(struct xfs_attri_log_nameval) + 89 + name_len + value_len); 90 + if (!nv) 91 + return nv; 92 + 93 + nv->name.i_addr = nv + 1; 94 + nv->name.i_len = name_len; 95 + nv->name.i_type = XLOG_REG_TYPE_ATTR_NAME; 96 + memcpy(nv->name.i_addr, name, name_len); 97 + 98 + if (value_len) { 99 + nv->value.i_addr = nv->name.i_addr + name_len; 100 + nv->value.i_len = value_len; 101 + memcpy(nv->value.i_addr, value, value_len); 102 + } else { 103 + nv->value.i_addr = NULL; 104 + nv->value.i_len = 0; 105 + } 106 + nv->value.i_type = XLOG_REG_TYPE_ATTR_VALUE; 107 + 108 + refcount_set(&nv->refcount, 1); 109 + return nv; 110 + } 111 + 45 112 STATIC void 46 113 xfs_attri_item_free( 47 114 struct xfs_attri_log_item *attrip) 48 115 { 49 116 kmem_free(attrip->attri_item.li_lv_shadow); 50 - kvfree(attrip); 117 + xfs_attri_log_nameval_put(attrip->attri_nameval); 118 + kmem_cache_free(xfs_attri_cache, attrip); 51 119 } 52 120 53 121 /* ··· 144 76 int *nbytes) 145 77 { 146 78 struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); 79 + struct xfs_attri_log_nameval *nv = attrip->attri_nameval; 147 80 148 81 *nvecs += 2; 149 82 *nbytes += sizeof(struct xfs_attri_log_format) + 150 - xlog_calc_iovec_len(attrip->attri_name_len); 83 + xlog_calc_iovec_len(nv->name.i_len); 151 84 152 - if (!attrip->attri_value_len) 85 + if (!nv->value.i_len) 153 86 return; 154 87 155 88 *nvecs += 1; 156 - *nbytes += xlog_calc_iovec_len(attrip->attri_value_len); 89 + *nbytes += xlog_calc_iovec_len(nv->value.i_len); 157 90 } 158 91 159 92 /* ··· 169 100 { 170 101 struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); 171 102 struct xfs_log_iovec *vecp = NULL; 103 + struct xfs_attri_log_nameval *nv = attrip->attri_nameval; 172 104 173 105 attrip->attri_format.alfi_type = XFS_LI_ATTRI; 174 106 attrip->attri_format.alfi_size = 1; ··· 181 111 * the log recovery. 182 112 */ 183 113 184 - ASSERT(attrip->attri_name_len > 0); 114 + ASSERT(nv->name.i_len > 0); 185 115 attrip->attri_format.alfi_size++; 186 116 187 - if (attrip->attri_value_len > 0) 117 + if (nv->value.i_len > 0) 188 118 attrip->attri_format.alfi_size++; 189 119 190 120 xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT, 191 121 &attrip->attri_format, 192 122 sizeof(struct xfs_attri_log_format)); 193 - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME, 194 - attrip->attri_name, 195 - attrip->attri_name_len); 196 - if (attrip->attri_value_len > 0) 197 - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE, 198 - attrip->attri_value, 199 - attrip->attri_value_len); 123 + xlog_copy_from_iovec(lv, &vecp, &nv->name); 124 + if (nv->value.i_len > 0) 125 + xlog_copy_from_iovec(lv, &vecp, &nv->value); 200 126 } 201 127 202 128 /* ··· 227 161 STATIC struct xfs_attri_log_item * 228 162 xfs_attri_init( 229 163 struct xfs_mount *mp, 230 - uint32_t name_len, 231 - uint32_t value_len) 232 - 164 + struct xfs_attri_log_nameval *nv) 233 165 { 234 166 struct xfs_attri_log_item *attrip; 235 - uint32_t buffer_size = name_len + value_len; 236 167 237 - if (buffer_size) { 238 - /* 239 - * This could be over 64kB in length, so we have to use 240 - * kvmalloc() for this. But kvmalloc() utterly sucks, so we 241 - * use own version. 242 - */ 243 - attrip = xlog_kvmalloc(sizeof(struct xfs_attri_log_item) + 244 - buffer_size); 245 - } else { 246 - attrip = kmem_cache_alloc(xfs_attri_cache, 247 - GFP_NOFS | __GFP_NOFAIL); 248 - } 249 - memset(attrip, 0, sizeof(struct xfs_attri_log_item)); 168 + attrip = kmem_cache_zalloc(xfs_attri_cache, GFP_NOFS | __GFP_NOFAIL); 250 169 251 - attrip->attri_name_len = name_len; 252 - if (name_len) 253 - attrip->attri_name = ((char *)attrip) + 254 - sizeof(struct xfs_attri_log_item); 255 - else 256 - attrip->attri_name = NULL; 257 - 258 - attrip->attri_value_len = value_len; 259 - if (value_len) 260 - attrip->attri_value = ((char *)attrip) + 261 - sizeof(struct xfs_attri_log_item) + 262 - name_len; 263 - else 264 - attrip->attri_value = NULL; 170 + /* 171 + * Grab an extra reference to the name/value buffer for this log item. 172 + * The caller retains its own reference! 173 + */ 174 + attrip->attri_nameval = xfs_attri_log_nameval_get(nv); 175 + ASSERT(attrip->attri_nameval); 265 176 266 177 xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI, 267 178 &xfs_attri_item_ops); ··· 397 354 attrp->alfi_ino = attr->xattri_da_args->dp->i_ino; 398 355 ASSERT(!(attr->xattri_op_flags & ~XFS_ATTRI_OP_FLAGS_TYPE_MASK)); 399 356 attrp->alfi_op_flags = attr->xattri_op_flags; 400 - attrp->alfi_value_len = attr->xattri_da_args->valuelen; 401 - attrp->alfi_name_len = attr->xattri_da_args->namelen; 357 + attrp->alfi_value_len = attr->xattri_nameval->value.i_len; 358 + attrp->alfi_name_len = attr->xattri_nameval->name.i_len; 402 359 ASSERT(!(attr->xattri_da_args->attr_filter & ~XFS_ATTRI_FILTER_MASK)); 403 360 attrp->alfi_attr_filter = attr->xattri_da_args->attr_filter; 404 - 405 - memcpy(attrip->attri_name, attr->xattri_da_args->name, 406 - attr->xattri_da_args->namelen); 407 - memcpy(attrip->attri_value, attr->xattri_da_args->value, 408 - attr->xattri_da_args->valuelen); 409 - attrip->attri_name_len = attr->xattri_da_args->namelen; 410 - attrip->attri_value_len = attr->xattri_da_args->valuelen; 411 361 } 412 362 413 363 /* Get an ATTRI. */ ··· 424 388 * Each attr item only performs one attribute operation at a time, so 425 389 * this is a list of one 426 390 */ 427 - list_for_each_entry(attr, items, xattri_list) { 428 - attrip = xfs_attri_init(mp, attr->xattri_da_args->namelen, 429 - attr->xattri_da_args->valuelen); 430 - if (attrip == NULL) 431 - return NULL; 391 + attr = list_first_entry_or_null(items, struct xfs_attr_intent, 392 + xattri_list); 432 393 433 - xfs_trans_add_item(tp, &attrip->attri_item); 434 - xfs_attr_log_item(tp, attrip, attr); 394 + /* 395 + * Create a buffer to store the attribute name and value. This buffer 396 + * will be shared between the higher level deferred xattr work state 397 + * and the lower level xattr log items. 398 + */ 399 + if (!attr->xattri_nameval) { 400 + struct xfs_da_args *args = attr->xattri_da_args; 401 + 402 + /* 403 + * Transfer our reference to the name/value buffer to the 404 + * deferred work state structure. 405 + */ 406 + attr->xattri_nameval = xfs_attri_log_nameval_alloc(args->name, 407 + args->namelen, args->value, args->valuelen); 435 408 } 409 + if (!attr->xattri_nameval) 410 + return ERR_PTR(-ENOMEM); 411 + 412 + attrip = xfs_attri_init(mp, attr->xattri_nameval); 413 + xfs_trans_add_item(tp, &attrip->attri_item); 414 + xfs_attr_log_item(tp, attrip, attr); 436 415 437 416 return &attrip->attri_item; 438 417 } ··· 458 407 { 459 408 if (attr->xattri_da_state) 460 409 xfs_da_state_free(attr->xattri_da_state); 410 + xfs_attri_log_nameval_put(attr->xattri_nameval); 461 411 if (attr->xattri_da_args->op_flags & XFS_DA_OP_RECOVERY) 462 412 kmem_free(attr); 463 413 else ··· 511 459 512 460 attr = container_of(item, struct xfs_attr_intent, xattri_list); 513 461 xfs_attr_free_item(attr); 514 - } 515 - 516 - STATIC xfs_lsn_t 517 - xfs_attri_item_committed( 518 - struct xfs_log_item *lip, 519 - xfs_lsn_t lsn) 520 - { 521 - struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); 522 - 523 - /* 524 - * The attrip refers to xfs_attr_item memory to log the name and value 525 - * with the intent item. This already occurred when the intent was 526 - * committed so these fields are no longer accessed. Clear them out of 527 - * caution since we're about to free the xfs_attr_item. 528 - */ 529 - attrip->attri_name = NULL; 530 - attrip->attri_value = NULL; 531 - 532 - /* 533 - * The ATTRI is logged only once and cannot be moved in the log, so 534 - * simply return the lsn at which it's been logged. 535 - */ 536 - return lsn; 537 462 } 538 463 539 464 STATIC bool ··· 576 547 struct xfs_trans *tp; 577 548 struct xfs_trans_res tres; 578 549 struct xfs_attri_log_format *attrp; 550 + struct xfs_attri_log_nameval *nv = attrip->attri_nameval; 579 551 int error, ret = 0; 580 552 int total; 581 553 int local; ··· 588 558 */ 589 559 attrp = &attrip->attri_format; 590 560 if (!xfs_attri_validate(mp, attrp) || 591 - !xfs_attr_namecheck(attrip->attri_name, attrip->attri_name_len)) 561 + !xfs_attr_namecheck(nv->name.i_addr, nv->name.i_len)) 592 562 return -EFSCORRUPTED; 593 563 594 564 error = xlog_recover_iget(mp, attrp->alfi_ino, &ip); ··· 603 573 attr->xattri_op_flags = attrp->alfi_op_flags & 604 574 XFS_ATTRI_OP_FLAGS_TYPE_MASK; 605 575 576 + /* 577 + * We're reconstructing the deferred work state structure from the 578 + * recovered log item. Grab a reference to the name/value buffer and 579 + * attach it to the new work state. 580 + */ 581 + attr->xattri_nameval = xfs_attri_log_nameval_get(nv); 582 + ASSERT(attr->xattri_nameval); 583 + 606 584 args->dp = ip; 607 585 args->geo = mp->m_attr_geo; 608 586 args->whichfork = XFS_ATTR_FORK; 609 - args->name = attrip->attri_name; 610 - args->namelen = attrp->alfi_name_len; 587 + args->name = nv->name.i_addr; 588 + args->namelen = nv->name.i_len; 611 589 args->hashval = xfs_da_hashname(args->name, args->namelen); 612 590 args->attr_filter = attrp->alfi_attr_filter & XFS_ATTRI_FILTER_MASK; 613 591 args->op_flags = XFS_DA_OP_RECOVERY | XFS_DA_OP_OKNOENT; ··· 623 585 switch (attr->xattri_op_flags) { 624 586 case XFS_ATTRI_OP_FLAGS_SET: 625 587 case XFS_ATTRI_OP_FLAGS_REPLACE: 626 - args->value = attrip->attri_value; 627 - args->valuelen = attrp->alfi_value_len; 588 + args->value = nv->value.i_addr; 589 + args->valuelen = nv->value.i_len; 628 590 args->total = xfs_attr_calc_size(args, &local); 629 591 if (xfs_inode_hasattr(args->dp)) 630 592 attr->xattri_dela_state = xfs_attr_init_replace_state(args); ··· 698 660 attrdp = xfs_trans_get_attrd(tp, old_attrip); 699 661 set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags); 700 662 701 - new_attrip = xfs_attri_init(tp->t_mountp, old_attrp->alfi_name_len, 702 - old_attrp->alfi_value_len); 663 + /* 664 + * Create a new log item that shares the same name/value buffer as the 665 + * old log item. 666 + */ 667 + new_attrip = xfs_attri_init(tp->t_mountp, old_attrip->attri_nameval); 703 668 new_attrp = &new_attrip->attri_format; 704 669 705 670 new_attrp->alfi_ino = old_attrp->alfi_ino; ··· 710 669 new_attrp->alfi_value_len = old_attrp->alfi_value_len; 711 670 new_attrp->alfi_name_len = old_attrp->alfi_name_len; 712 671 new_attrp->alfi_attr_filter = old_attrp->alfi_attr_filter; 713 - 714 - memcpy(new_attrip->attri_name, old_attrip->attri_name, 715 - new_attrip->attri_name_len); 716 - 717 - if (new_attrip->attri_value_len > 0) 718 - memcpy(new_attrip->attri_value, old_attrip->attri_value, 719 - new_attrip->attri_value_len); 720 672 721 673 xfs_trans_add_item(tp, &new_attrip->attri_item); 722 674 set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags); ··· 724 690 struct xlog_recover_item *item, 725 691 xfs_lsn_t lsn) 726 692 { 727 - int error; 728 693 struct xfs_mount *mp = log->l_mp; 729 694 struct xfs_attri_log_item *attrip; 730 695 struct xfs_attri_log_format *attri_formatp; 696 + struct xfs_attri_log_nameval *nv; 697 + const void *attr_value = NULL; 731 698 const void *attr_name; 732 - int region = 0; 699 + int error; 733 700 734 - attri_formatp = item->ri_buf[region].i_addr; 701 + attri_formatp = item->ri_buf[0].i_addr; 735 702 attr_name = item->ri_buf[1].i_addr; 736 703 737 704 /* Validate xfs_attri_log_format before the large memory allocation */ ··· 746 711 return -EFSCORRUPTED; 747 712 } 748 713 749 - /* memory alloc failure will cause replay to abort */ 750 - attrip = xfs_attri_init(mp, attri_formatp->alfi_name_len, 751 - attri_formatp->alfi_value_len); 752 - if (attrip == NULL) 714 + if (attri_formatp->alfi_value_len) 715 + attr_value = item->ri_buf[2].i_addr; 716 + 717 + /* 718 + * Memory alloc failure will cause replay to abort. We attach the 719 + * name/value buffer to the recovered incore log item and drop our 720 + * reference. 721 + */ 722 + nv = xfs_attri_log_nameval_alloc(attr_name, 723 + attri_formatp->alfi_name_len, attr_value, 724 + attri_formatp->alfi_value_len); 725 + if (!nv) 753 726 return -ENOMEM; 754 727 755 - error = xfs_attri_copy_format(&item->ri_buf[region], 756 - &attrip->attri_format); 728 + attrip = xfs_attri_init(mp, nv); 729 + error = xfs_attri_copy_format(&item->ri_buf[0], &attrip->attri_format); 757 730 if (error) 758 731 goto out; 759 - 760 - region++; 761 - memcpy(attrip->attri_name, item->ri_buf[region].i_addr, 762 - attrip->attri_name_len); 763 - 764 - if (attrip->attri_value_len > 0) { 765 - region++; 766 - memcpy(attrip->attri_value, item->ri_buf[region].i_addr, 767 - attrip->attri_value_len); 768 - } 769 732 770 733 /* 771 734 * The ATTRI has two references. One for the ATTRD and one for ATTRI to ··· 773 740 */ 774 741 xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn); 775 742 xfs_attri_release(attrip); 743 + xfs_attri_log_nameval_put(nv); 776 744 return 0; 777 745 out: 778 746 xfs_attri_item_free(attrip); 747 + xfs_attri_log_nameval_put(nv); 779 748 return error; 780 749 } 781 750 ··· 857 822 .iop_size = xfs_attri_item_size, 858 823 .iop_format = xfs_attri_item_format, 859 824 .iop_unpin = xfs_attri_item_unpin, 860 - .iop_committed = xfs_attri_item_committed, 861 825 .iop_release = xfs_attri_item_release, 862 826 .iop_recover = xfs_attri_item_recover, 863 827 .iop_match = xfs_attri_item_match,
+9 -4
fs/xfs/xfs_attr_item.h
··· 11 11 struct xfs_mount; 12 12 struct kmem_zone; 13 13 14 + struct xfs_attri_log_nameval { 15 + struct xfs_log_iovec name; 16 + struct xfs_log_iovec value; 17 + refcount_t refcount; 18 + 19 + /* name and value follow the end of this struct */ 20 + }; 21 + 14 22 /* 15 23 * This is the "attr intention" log item. It is used to log the fact that some 16 24 * extended attribute operations need to be processed. An operation is ··· 34 26 struct xfs_attri_log_item { 35 27 struct xfs_log_item attri_item; 36 28 atomic_t attri_refcount; 37 - int attri_name_len; 38 - int attri_value_len; 39 - void *attri_name; 40 - void *attri_value; 29 + struct xfs_attri_log_nameval *attri_nameval; 41 30 struct xfs_attri_log_format attri_format; 42 31 }; 43 32
+7
fs/xfs/xfs_log.h
··· 86 86 return buf; 87 87 } 88 88 89 + static inline void * 90 + xlog_copy_from_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp, 91 + const struct xfs_log_iovec *src) 92 + { 93 + return xlog_copy_iovec(lv, vecp, src->i_type, src->i_addr, src->i_len); 94 + } 95 + 89 96 /* 90 97 * By comparing each component, we don't have to worry about extra 91 98 * endian issues in treating two 32 bit numbers as one 64 bit number