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

[PATCH] 9p: fix segfault caused by race condition in meta-data operations

Running dbench multithreaded exposed a race condition where fid structures
were removed while in use. This patch adds semaphores to meta-data operations
to protect the fid structure. Some cleanup of error-case handling in the
inode operations is also included.

Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Eric Van Hensbergen and committed by
Linus Torvalds
da977b2c ff76e1df

+196 -129
+66 -3
fs/9p/fid.c
··· 25 25 #include <linux/fs.h> 26 26 #include <linux/sched.h> 27 27 #include <linux/idr.h> 28 + #include <asm/semaphore.h> 28 29 29 30 #include "debug.h" 30 31 #include "v9fs.h" ··· 85 84 new->iounit = 0; 86 85 new->rdir_pos = 0; 87 86 new->rdir_fcall = NULL; 87 + init_MUTEX(&new->lock); 88 88 INIT_LIST_HEAD(&new->list); 89 89 90 90 return new; ··· 104 102 } 105 103 106 104 /** 107 - * v9fs_fid_lookup - retrieve the right fid from a particular dentry 105 + * v9fs_fid_lookup - return a locked fid from a dentry 108 106 * @dentry: dentry to look for fid in 109 - * @type: intent of lookup (operation or traversal) 110 107 * 111 - * find a fid in the dentry 108 + * find a fid in the dentry, obtain its semaphore and return a reference to it. 109 + * code calling lookup is responsible for releasing lock 112 110 * 113 111 * TODO: only match fids that have the same uid as current user 114 112 * ··· 126 124 127 125 if (!return_fid) { 128 126 dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); 127 + return_fid = ERR_PTR(-EBADF); 129 128 } 130 129 130 + if(down_interruptible(&return_fid->lock)) 131 + return ERR_PTR(-EINTR); 132 + 131 133 return return_fid; 134 + } 135 + 136 + /** 137 + * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it 138 + * @dentry: dentry to look for fid in 139 + * 140 + * find a fid in the dentry and then clone to a new private fid 141 + * 142 + * TODO: only match fids that have the same uid as current user 143 + * 144 + */ 145 + 146 + struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry) 147 + { 148 + struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 149 + struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF); 150 + struct v9fs_fcall *fcall = NULL; 151 + int fid, err; 152 + 153 + base_fid = v9fs_fid_lookup(dentry); 154 + 155 + if(IS_ERR(base_fid)) 156 + return base_fid; 157 + 158 + if(base_fid) { /* clone fid */ 159 + fid = v9fs_get_idpool(&v9ses->fidpool); 160 + if (fid < 0) { 161 + eprintk(KERN_WARNING, "newfid fails!\n"); 162 + new_fid = ERR_PTR(-ENOSPC); 163 + goto Release_Fid; 164 + } 165 + 166 + err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall); 167 + if (err < 0) { 168 + dprintk(DEBUG_ERROR, "clone walk didn't work\n"); 169 + v9fs_put_idpool(fid, &v9ses->fidpool); 170 + new_fid = ERR_PTR(err); 171 + goto Free_Fcall; 172 + } 173 + new_fid = v9fs_fid_create(v9ses, fid); 174 + if (new_fid == NULL) { 175 + dprintk(DEBUG_ERROR, "out of memory\n"); 176 + new_fid = ERR_PTR(-ENOMEM); 177 + } 178 + Free_Fcall: 179 + kfree(fcall); 180 + } 181 + 182 + Release_Fid: 183 + up(&base_fid->lock); 184 + return new_fid; 185 + } 186 + 187 + void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid) 188 + { 189 + v9fs_t_clunk(v9ses, fid->fid); 190 + v9fs_fid_destroy(fid); 132 191 }
+5
fs/9p/fid.h
··· 30 30 struct list_head list; /* list of fids associated with a dentry */ 31 31 struct list_head active; /* XXX - debug */ 32 32 33 + struct semaphore lock; 34 + 33 35 u32 fid; 34 36 unsigned char fidopen; /* set when fid is opened */ 35 37 unsigned char fidclunked; /* set when fid has already been clunked */ ··· 57 55 void v9fs_fid_destroy(struct v9fs_fid *fid); 58 56 struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); 59 57 int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); 58 + struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry); 59 + void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid); 60 +
+7 -40
fs/9p/vfs_file.c
··· 55 55 struct v9fs_fid *vfid; 56 56 struct v9fs_fcall *fcall = NULL; 57 57 int omode; 58 - int fid = V9FS_NOFID; 59 58 int err; 60 59 61 60 dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); 62 61 63 - vfid = v9fs_fid_lookup(file->f_path.dentry); 64 - if (!vfid) { 65 - dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n"); 66 - return -EBADF; 67 - } 62 + vfid = v9fs_fid_clone(file->f_path.dentry); 63 + if (IS_ERR(vfid)) 64 + return PTR_ERR(vfid); 68 65 69 - fid = v9fs_get_idpool(&v9ses->fidpool); 70 - if (fid < 0) { 71 - eprintk(KERN_WARNING, "newfid fails!\n"); 72 - return -ENOSPC; 73 - } 74 - 75 - err = v9fs_t_walk(v9ses, vfid->fid, fid, NULL, &fcall); 76 - if (err < 0) { 77 - dprintk(DEBUG_ERROR, "rewalk didn't work\n"); 78 - if (fcall && fcall->id == RWALK) 79 - goto clunk_fid; 80 - else { 81 - v9fs_put_idpool(fid, &v9ses->fidpool); 82 - goto free_fcall; 83 - } 84 - } 85 - kfree(fcall); 86 - 87 - /* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */ 88 - /* translate open mode appropriately */ 89 66 omode = v9fs_uflags2omode(file->f_flags); 90 - err = v9fs_t_open(v9ses, fid, omode, &fcall); 67 + err = v9fs_t_open(v9ses, vfid->fid, omode, &fcall); 91 68 if (err < 0) { 92 69 PRINT_FCALL_ERROR("open failed", fcall); 93 - goto clunk_fid; 94 - } 95 - 96 - vfid = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); 97 - if (vfid == NULL) { 98 - dprintk(DEBUG_ERROR, "out of memory\n"); 99 - err = -ENOMEM; 100 - goto clunk_fid; 70 + goto Clunk_Fid; 101 71 } 102 72 103 73 file->private_data = vfid; 104 - vfid->fid = fid; 105 74 vfid->fidopen = 1; 106 75 vfid->fidclunked = 0; 107 76 vfid->iounit = fcall->params.ropen.iounit; ··· 81 112 82 113 return 0; 83 114 84 - clunk_fid: 85 - v9fs_t_clunk(v9ses, fid); 86 - 87 - free_fcall: 115 + Clunk_Fid: 116 + v9fs_fid_clunk(v9ses, vfid); 88 117 kfree(fcall); 89 118 90 119 return err;
+118 -86
fs/9p/vfs_inode.c
··· 416 416 sb = file_inode->i_sb; 417 417 v9ses = v9fs_inode2v9ses(file_inode); 418 418 v9fid = v9fs_fid_lookup(file); 419 - 420 - if (!v9fid) { 421 - dprintk(DEBUG_ERROR, 422 - "no v9fs_fid\n"); 423 - return -EBADF; 424 - } 419 + if(IS_ERR(v9fid)) 420 + return PTR_ERR(v9fid); 425 421 426 422 fid = v9fid->fid; 427 423 if (fid < 0) { ··· 429 433 result = v9fs_t_remove(v9ses, fid, &fcall); 430 434 if (result < 0) { 431 435 PRINT_FCALL_ERROR("remove fails", fcall); 436 + goto Error; 432 437 } 433 438 434 439 v9fs_put_idpool(fid, &v9ses->fidpool); 435 440 v9fs_fid_destroy(v9fid); 436 441 442 + Error: 437 443 kfree(fcall); 438 444 return result; 439 445 } ··· 471 473 inode = NULL; 472 474 vfid = NULL; 473 475 v9ses = v9fs_inode2v9ses(dir); 474 - dfid = v9fs_fid_lookup(dentry->d_parent); 475 - perm = unixmode2p9mode(v9ses, mode); 476 + dfid = v9fs_fid_clone(dentry->d_parent); 477 + if(IS_ERR(dfid)) { 478 + err = PTR_ERR(dfid); 479 + goto error; 480 + } 476 481 482 + perm = unixmode2p9mode(v9ses, mode); 477 483 if (nd && nd->flags & LOOKUP_OPEN) 478 484 flags = nd->intent.open.flags - 1; 479 485 else ··· 487 485 perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit); 488 486 489 487 if (err) 490 - goto error; 488 + goto clunk_dfid; 491 489 492 490 vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); 491 + v9fs_fid_clunk(v9ses, dfid); 493 492 if (IS_ERR(vfid)) { 494 493 err = PTR_ERR(vfid); 495 494 vfid = NULL; ··· 528 525 529 526 return 0; 530 527 528 + clunk_dfid: 529 + v9fs_fid_clunk(v9ses, dfid); 530 + 531 531 error: 532 532 if (vfid) 533 533 v9fs_fid_destroy(vfid); ··· 557 551 inode = NULL; 558 552 vfid = NULL; 559 553 v9ses = v9fs_inode2v9ses(dir); 560 - dfid = v9fs_fid_lookup(dentry->d_parent); 554 + dfid = v9fs_fid_clone(dentry->d_parent); 555 + if(IS_ERR(dfid)) { 556 + err = PTR_ERR(dfid); 557 + goto error; 558 + } 559 + 561 560 perm = unixmode2p9mode(v9ses, mode | S_IFDIR); 562 561 563 562 err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, ··· 570 559 571 560 if (err) { 572 561 dprintk(DEBUG_ERROR, "create error %d\n", err); 573 - goto error; 574 - } 575 - 576 - err = v9fs_t_clunk(v9ses, fid); 577 - if (err) { 578 - dprintk(DEBUG_ERROR, "clunk error %d\n", err); 579 - goto error; 562 + goto clean_up_dfid; 580 563 } 581 564 582 565 vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); 583 566 if (IS_ERR(vfid)) { 584 567 err = PTR_ERR(vfid); 585 568 vfid = NULL; 586 - goto error; 569 + goto clean_up_dfid; 587 570 } 588 571 572 + v9fs_fid_clunk(v9ses, dfid); 589 573 inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); 590 574 if (IS_ERR(inode)) { 591 575 err = PTR_ERR(inode); 592 576 inode = NULL; 593 - goto error; 577 + goto clean_up_fids; 594 578 } 595 579 596 580 dentry->d_op = &v9fs_dentry_operations; 597 581 d_instantiate(dentry, inode); 598 582 return 0; 599 583 600 - error: 584 + clean_up_fids: 601 585 if (vfid) 602 586 v9fs_fid_destroy(vfid); 603 587 588 + clean_up_dfid: 589 + v9fs_fid_clunk(v9ses, dfid); 590 + 591 + error: 604 592 return err; 605 593 } 606 594 ··· 632 622 dentry->d_op = &v9fs_dentry_operations; 633 623 dirfid = v9fs_fid_lookup(dentry->d_parent); 634 624 635 - if (!dirfid) { 636 - dprintk(DEBUG_ERROR, "no dirfid\n"); 637 - return ERR_PTR(-EINVAL); 638 - } 625 + if(IS_ERR(dirfid)) 626 + return ERR_PTR(PTR_ERR(dirfid)); 639 627 640 628 dirfidnum = dirfid->fid; 641 - 642 - if (dirfidnum < 0) { 643 - dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n", 644 - dir, dir->i_ino); 645 - return ERR_PTR(-EBADF); 646 - } 647 629 648 630 newfid = v9fs_get_idpool(&v9ses->fidpool); 649 631 if (newfid < 0) { 650 632 eprintk(KERN_WARNING, "newfid fails!\n"); 651 - return ERR_PTR(-ENOSPC); 633 + result = -ENOSPC; 634 + goto Release_Dirfid; 652 635 } 653 636 654 637 result = v9fs_t_walk(v9ses, dirfidnum, newfid, 655 638 (char *)dentry->d_name.name, &fcall); 639 + 640 + up(&dirfid->lock); 656 641 657 642 if (result < 0) { 658 643 if (fcall && fcall->id == RWALK) ··· 706 701 707 702 return NULL; 708 703 709 - FreeFcall: 704 + Release_Dirfid: 705 + up(&dirfid->lock); 706 + 707 + FreeFcall: 710 708 kfree(fcall); 709 + 711 710 return ERR_PTR(result); 712 711 } 713 712 ··· 755 746 struct inode *old_inode = old_dentry->d_inode; 756 747 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); 757 748 struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); 758 - struct v9fs_fid *olddirfid = 759 - v9fs_fid_lookup(old_dentry->d_parent); 760 - struct v9fs_fid *newdirfid = 761 - v9fs_fid_lookup(new_dentry->d_parent); 749 + struct v9fs_fid *olddirfid; 750 + struct v9fs_fid *newdirfid; 762 751 struct v9fs_wstat wstat; 763 752 struct v9fs_fcall *fcall = NULL; 764 753 int fid = -1; ··· 766 759 767 760 dprintk(DEBUG_VFS, "\n"); 768 761 769 - if ((!oldfid) || (!olddirfid) || (!newdirfid)) { 770 - dprintk(DEBUG_ERROR, "problem with arguments\n"); 771 - return -EBADF; 762 + if(IS_ERR(oldfid)) 763 + return PTR_ERR(oldfid); 764 + 765 + olddirfid = v9fs_fid_clone(old_dentry->d_parent); 766 + if(IS_ERR(olddirfid)) { 767 + retval = PTR_ERR(olddirfid); 768 + goto Release_lock; 769 + } 770 + 771 + newdirfid = v9fs_fid_clone(new_dentry->d_parent); 772 + if(IS_ERR(newdirfid)) { 773 + retval = PTR_ERR(newdirfid); 774 + goto Clunk_olddir; 772 775 } 773 776 774 777 /* 9P can only handle file rename in the same directory */ 775 778 if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { 776 779 dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); 777 780 retval = -EXDEV; 778 - goto FreeFcallnBail; 781 + goto Clunk_newdir; 779 782 } 780 783 781 784 fid = oldfid->fid; ··· 796 779 dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", 797 780 old_inode->i_ino); 798 781 retval = -EBADF; 799 - goto FreeFcallnBail; 782 + goto Clunk_newdir; 800 783 } 801 784 802 785 v9fs_blank_wstat(&wstat); ··· 805 788 806 789 retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); 807 790 808 - FreeFcallnBail: 809 791 if (retval < 0) 810 792 PRINT_FCALL_ERROR("wstat error", fcall); 811 793 812 794 kfree(fcall); 795 + 796 + Clunk_newdir: 797 + v9fs_fid_clunk(v9ses, newdirfid); 798 + 799 + Clunk_olddir: 800 + v9fs_fid_clunk(v9ses, olddirfid); 801 + 802 + Release_lock: 803 + up(&oldfid->lock); 804 + 813 805 return retval; 814 806 } 815 807 ··· 836 810 { 837 811 struct v9fs_fcall *fcall = NULL; 838 812 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 839 - struct v9fs_fid *fid = v9fs_fid_lookup(dentry); 813 + struct v9fs_fid *fid = v9fs_fid_clone(dentry); 840 814 int err = -EPERM; 841 815 842 816 dprintk(DEBUG_VFS, "dentry: %p\n", dentry); 843 - if (!fid) { 844 - dprintk(DEBUG_ERROR, 845 - "couldn't find fid associated with dentry\n"); 846 - return -EBADF; 847 - } 817 + if(IS_ERR(fid)) 818 + return PTR_ERR(fid); 848 819 849 820 err = v9fs_t_stat(v9ses, fid->fid, &fcall); 850 821 ··· 854 831 } 855 832 856 833 kfree(fcall); 834 + v9fs_fid_clunk(v9ses, fid); 857 835 return err; 858 836 } 859 837 ··· 868 844 static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) 869 845 { 870 846 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 871 - struct v9fs_fid *fid = v9fs_fid_lookup(dentry); 847 + struct v9fs_fid *fid = v9fs_fid_clone(dentry); 872 848 struct v9fs_fcall *fcall = NULL; 873 849 struct v9fs_wstat wstat; 874 850 int res = -EPERM; 875 851 876 852 dprintk(DEBUG_VFS, "\n"); 877 - 878 - if (!fid) { 879 - dprintk(DEBUG_ERROR, 880 - "Couldn't find fid associated with dentry\n"); 881 - return -EBADF; 882 - } 853 + if(IS_ERR(fid)) 854 + return PTR_ERR(fid); 883 855 884 856 v9fs_blank_wstat(&wstat); 885 857 if (iattr->ia_valid & ATTR_MODE) ··· 907 887 if (res >= 0) 908 888 res = inode_setattr(dentry->d_inode, iattr); 909 889 890 + v9fs_fid_clunk(v9ses, fid); 910 891 return res; 911 892 } 912 893 ··· 1008 987 1009 988 struct v9fs_fcall *fcall = NULL; 1010 989 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 1011 - struct v9fs_fid *fid = v9fs_fid_lookup(dentry); 990 + struct v9fs_fid *fid = v9fs_fid_clone(dentry); 1012 991 1013 - if (!fid) { 1014 - dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n"); 1015 - retval = -EBADF; 1016 - goto FreeFcall; 1017 - } 992 + if(IS_ERR(fid)) 993 + return PTR_ERR(fid); 1018 994 1019 995 if (!v9ses->extended) { 1020 996 retval = -EBADF; 1021 997 dprintk(DEBUG_ERROR, "not extended\n"); 1022 - goto FreeFcall; 998 + goto ClunkFid; 1023 999 } 1024 1000 1025 1001 dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); ··· 1027 1009 goto FreeFcall; 1028 1010 } 1029 1011 1030 - if (!fcall) 1031 - return -EIO; 1012 + if (!fcall) { 1013 + retval = -EIO; 1014 + goto ClunkFid; 1015 + } 1032 1016 1033 1017 if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { 1034 1018 retval = -EINVAL; ··· 1048 1028 fcall->params.rstat.stat.extension.str, buffer); 1049 1029 retval = buflen; 1050 1030 1051 - FreeFcall: 1031 + FreeFcall: 1052 1032 kfree(fcall); 1033 + 1034 + ClunkFid: 1035 + v9fs_fid_clunk(v9ses, fid); 1053 1036 1054 1037 return retval; 1055 1038 } ··· 1146 1123 int err; 1147 1124 u32 fid, perm; 1148 1125 struct v9fs_session_info *v9ses; 1149 - struct v9fs_fid *dfid, *vfid; 1150 - struct inode *inode; 1126 + struct v9fs_fid *dfid, *vfid = NULL; 1127 + struct inode *inode = NULL; 1151 1128 1152 - inode = NULL; 1153 - vfid = NULL; 1154 1129 v9ses = v9fs_inode2v9ses(dir); 1155 - dfid = v9fs_fid_lookup(dentry->d_parent); 1156 - perm = unixmode2p9mode(v9ses, mode); 1157 - 1158 1130 if (!v9ses->extended) { 1159 1131 dprintk(DEBUG_ERROR, "not extended\n"); 1160 1132 return -EPERM; 1161 1133 } 1162 1134 1135 + dfid = v9fs_fid_clone(dentry->d_parent); 1136 + if(IS_ERR(dfid)) { 1137 + err = PTR_ERR(dfid); 1138 + goto error; 1139 + } 1140 + 1141 + perm = unixmode2p9mode(v9ses, mode); 1142 + 1163 1143 err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, 1164 1144 perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL); 1165 1145 1166 1146 if (err) 1167 - goto error; 1147 + goto clunk_dfid; 1168 1148 1169 1149 err = v9fs_t_clunk(v9ses, fid); 1170 1150 if (err) 1171 - goto error; 1151 + goto clunk_dfid; 1172 1152 1173 1153 vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); 1174 1154 if (IS_ERR(vfid)) { 1175 1155 err = PTR_ERR(vfid); 1176 1156 vfid = NULL; 1177 - goto error; 1157 + goto clunk_dfid; 1178 1158 } 1179 1159 1180 1160 inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); 1181 1161 if (IS_ERR(inode)) { 1182 1162 err = PTR_ERR(inode); 1183 1163 inode = NULL; 1184 - goto error; 1164 + goto free_vfid; 1185 1165 } 1186 1166 1187 1167 dentry->d_op = &v9fs_dentry_operations; 1188 1168 d_instantiate(dentry, inode); 1189 1169 return 0; 1190 1170 1191 - error: 1192 - if (vfid) 1193 - v9fs_fid_destroy(vfid); 1171 + free_vfid: 1172 + v9fs_fid_destroy(vfid); 1194 1173 1174 + clunk_dfid: 1175 + v9fs_fid_clunk(v9ses, dfid); 1176 + 1177 + error: 1195 1178 return err; 1196 1179 1197 1180 } ··· 1238 1209 struct dentry *dentry) 1239 1210 { 1240 1211 int retval; 1212 + struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); 1241 1213 struct v9fs_fid *oldfid; 1242 1214 char *name; 1243 1215 1244 1216 dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, 1245 1217 old_dentry->d_name.name); 1246 1218 1247 - oldfid = v9fs_fid_lookup(old_dentry); 1248 - if (!oldfid) { 1249 - dprintk(DEBUG_ERROR, "can't find oldfid\n"); 1250 - return -EPERM; 1251 - } 1219 + oldfid = v9fs_fid_clone(old_dentry); 1220 + if(IS_ERR(oldfid)) 1221 + return PTR_ERR(oldfid); 1252 1222 1253 1223 name = __getname(); 1254 - if (unlikely(!name)) 1255 - return -ENOMEM; 1224 + if (unlikely(!name)) { 1225 + retval = -ENOMEM; 1226 + goto clunk_fid; 1227 + } 1256 1228 1257 1229 sprintf(name, "%d\n", oldfid->fid); 1258 1230 retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); 1259 1231 __putname(name); 1260 1232 1233 + clunk_fid: 1234 + v9fs_fid_clunk(v9ses, oldfid); 1261 1235 return retval; 1262 1236 } 1263 1237