Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs:
9p: fix readdir corner cases
9p: fix readlink
9p: fix a small bug in readdir for long directories

+72 -34
+65 -24
fs/9p/vfs_dir.c
··· 40 40 #include "fid.h" 41 41 42 42 /** 43 + * struct p9_rdir - readdir accounting 44 + * @mutex: mutex protecting readdir 45 + * @head: start offset of current dirread buffer 46 + * @tail: end offset of current dirread buffer 47 + * @buf: dirread buffer 48 + * 49 + * private structure for keeping track of readdir 50 + * allocated on demand 51 + */ 52 + 53 + struct p9_rdir { 54 + struct mutex mutex; 55 + int head; 56 + int tail; 57 + uint8_t *buf; 58 + }; 59 + 60 + /** 43 61 * dt_type - return file type 44 62 * @mistat: mistat structure 45 63 * ··· 88 70 { 89 71 int over; 90 72 struct p9_wstat st; 91 - int err; 73 + int err = 0; 92 74 struct p9_fid *fid; 93 75 int buflen; 94 - char *statbuf; 95 - int n, i = 0; 76 + int reclen = 0; 77 + struct p9_rdir *rdir; 96 78 97 79 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); 98 80 fid = filp->private_data; 99 81 100 82 buflen = fid->clnt->msize - P9_IOHDRSZ; 101 - statbuf = kmalloc(buflen, GFP_KERNEL); 102 - if (!statbuf) 103 - return -ENOMEM; 104 83 105 - while (1) { 106 - err = v9fs_file_readn(filp, statbuf, NULL, buflen, 107 - fid->rdir_fpos); 108 - if (err <= 0) 109 - break; 84 + /* allocate rdir on demand */ 85 + if (!fid->rdir) { 86 + rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); 110 87 111 - n = err; 112 - while (i < n) { 113 - err = p9stat_read(statbuf + i, buflen-i, &st, 114 - fid->clnt->dotu); 88 + if (rdir == NULL) { 89 + err = -ENOMEM; 90 + goto exit; 91 + } 92 + spin_lock(&filp->f_dentry->d_lock); 93 + if (!fid->rdir) { 94 + rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir); 95 + mutex_init(&rdir->mutex); 96 + rdir->head = rdir->tail = 0; 97 + fid->rdir = (void *) rdir; 98 + rdir = NULL; 99 + } 100 + spin_unlock(&filp->f_dentry->d_lock); 101 + kfree(rdir); 102 + } 103 + rdir = (struct p9_rdir *) fid->rdir; 104 + 105 + err = mutex_lock_interruptible(&rdir->mutex); 106 + while (err == 0) { 107 + if (rdir->tail == rdir->head) { 108 + err = v9fs_file_readn(filp, rdir->buf, NULL, 109 + buflen, filp->f_pos); 110 + if (err <= 0) 111 + goto unlock_and_exit; 112 + 113 + rdir->head = 0; 114 + rdir->tail = err; 115 + } 116 + 117 + while (rdir->head < rdir->tail) { 118 + err = p9stat_read(rdir->buf + rdir->head, 119 + buflen - rdir->head, &st, 120 + fid->clnt->dotu); 115 121 if (err) { 116 122 P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); 117 123 err = -EIO; 118 124 p9stat_free(&st); 119 - goto free_and_exit; 125 + goto unlock_and_exit; 120 126 } 121 - 122 - i += st.size+2; 123 - fid->rdir_fpos += st.size+2; 127 + reclen = st.size+2; 124 128 125 129 over = filldir(dirent, st.name, strlen(st.name), 126 130 filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st)); 127 - 128 - filp->f_pos += st.size+2; 129 131 130 132 p9stat_free(&st); 131 133 132 134 if (over) { 133 135 err = 0; 134 - goto free_and_exit; 136 + goto unlock_and_exit; 135 137 } 138 + rdir->head += reclen; 139 + filp->f_pos += reclen; 136 140 } 137 141 } 138 142 139 - free_and_exit: 140 - kfree(statbuf); 143 + unlock_and_exit: 144 + mutex_unlock(&rdir->mutex); 145 + exit: 141 146 return err; 142 147 } 143 148
+2 -3
fs/9p/vfs_inode.c
··· 994 994 P9_DPRINTK(P9_DEBUG_VFS, 995 995 "%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer); 996 996 997 - retval = buflen; 998 - 997 + retval = strnlen(buffer, buflen); 999 998 done: 1000 999 kfree(st); 1001 1000 return retval; ··· 1061 1062 __putname(link); 1062 1063 link = ERR_PTR(len); 1063 1064 } else 1064 - link[len] = 0; 1065 + link[min(len, PATH_MAX-1)] = 0; 1065 1066 } 1066 1067 nd_set_link(nd, link); 1067 1068
+3 -4
include/net/9p/client.h
··· 159 159 * @qid: the &p9_qid server identifier this handle points to 160 160 * @iounit: the server reported maximum transaction size for this file 161 161 * @uid: the numeric uid of the local user who owns this handle 162 - * @aux: transport specific information (unused?) 163 - * @rdir_fpos: tracks offset of file position when reading directory contents 162 + * @rdir: readdir accounting structure (allocated on demand) 164 163 * @flist: per-client-instance fid tracking 165 164 * @dlist: per-dentry fid tracking 166 165 * ··· 173 174 struct p9_qid qid; 174 175 u32 iounit; 175 176 uid_t uid; 176 - void *aux; 177 177 178 - int rdir_fpos; 178 + void *rdir; 179 + 179 180 struct list_head flist; 180 181 struct list_head dlist; /* list of all fids attached to a dentry */ 181 182 };
+2 -3
net/9p/client.c
··· 582 582 583 583 memset(&fid->qid, 0, sizeof(struct p9_qid)); 584 584 fid->mode = -1; 585 - fid->rdir_fpos = 0; 586 585 fid->uid = current_fsuid(); 587 586 fid->clnt = clnt; 588 - fid->aux = NULL; 589 - 587 + fid->rdir = NULL; 590 588 spin_lock_irqsave(&clnt->lock, flags); 591 589 list_add(&fid->flist, &clnt->fidlist); 592 590 spin_unlock_irqrestore(&clnt->lock, flags); ··· 607 609 spin_lock_irqsave(&clnt->lock, flags); 608 610 list_del(&fid->flist); 609 611 spin_unlock_irqrestore(&clnt->lock, flags); 612 + kfree(fid->rdir); 610 613 kfree(fid); 611 614 } 612 615