Commit 3e2796a90cf349527e50b3bc4d0b2f4019b1ce7a
1 parent
2511cd0b3b
Exists in
master
and in
39 other branches
9p: fix readdir corner cases
The patch below also addresses a couple of other corner cases in readdir seen with a large (e.g. 64k) msize. I'm not sure what people think of my co-opting of fid->aux here. I'd be happy to rework if there's a better way. When the size of the user supplied buffer passed to readdir is smaller than the data returned in one go by the 9P read request, v9fs_dir_readdir() currently discards extra data so that, on the next call, a 9P read request will be issued with offset < previous offset + bytes returned, which voilates the constraint described in paragraph 3 of read(5) description. This patch preseves the leftover data in fid->aux for use in the next call. Signed-off-by: Jim Garlick <garlick@llnl.gov> Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Showing 3 changed files with 72 additions and 34 deletions Side-by-side Diff
fs/9p/vfs_dir.c
... | ... | @@ -40,6 +40,24 @@ |
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 | * |
46 | 64 | |
47 | 65 | |
48 | 66 | |
49 | 67 | |
50 | 68 | |
51 | 69 | |
52 | 70 | |
53 | 71 | |
54 | 72 | |
55 | 73 | |
56 | 74 | |
... | ... | @@ -70,57 +88,79 @@ |
70 | 88 | { |
71 | 89 | int over; |
72 | 90 | struct p9_wstat st; |
73 | - int err; | |
91 | + int err = 0; | |
74 | 92 | struct p9_fid *fid; |
75 | 93 | int buflen; |
76 | - char *statbuf; | |
77 | - int n, i = 0; | |
94 | + int reclen = 0; | |
95 | + struct p9_rdir *rdir; | |
78 | 96 | |
79 | 97 | P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); |
80 | 98 | fid = filp->private_data; |
81 | 99 | |
82 | 100 | buflen = fid->clnt->msize - P9_IOHDRSZ; |
83 | - statbuf = kmalloc(buflen, GFP_KERNEL); | |
84 | - if (!statbuf) | |
85 | - return -ENOMEM; | |
86 | 101 | |
87 | - while (1) { | |
88 | - err = v9fs_file_readn(filp, statbuf, NULL, buflen, | |
89 | - fid->rdir_fpos); | |
90 | - if (err <= 0) | |
91 | - break; | |
102 | + /* allocate rdir on demand */ | |
103 | + if (!fid->rdir) { | |
104 | + rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); | |
92 | 105 | |
93 | - i = 0; | |
94 | - n = err; | |
95 | - while (i < n) { | |
96 | - err = p9stat_read(statbuf + i, buflen-i, &st, | |
97 | - fid->clnt->dotu); | |
106 | + if (rdir == NULL) { | |
107 | + err = -ENOMEM; | |
108 | + goto exit; | |
109 | + } | |
110 | + spin_lock(&filp->f_dentry->d_lock); | |
111 | + if (!fid->rdir) { | |
112 | + rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir); | |
113 | + mutex_init(&rdir->mutex); | |
114 | + rdir->head = rdir->tail = 0; | |
115 | + fid->rdir = (void *) rdir; | |
116 | + rdir = NULL; | |
117 | + } | |
118 | + spin_unlock(&filp->f_dentry->d_lock); | |
119 | + kfree(rdir); | |
120 | + } | |
121 | + rdir = (struct p9_rdir *) fid->rdir; | |
122 | + | |
123 | + err = mutex_lock_interruptible(&rdir->mutex); | |
124 | + while (err == 0) { | |
125 | + if (rdir->tail == rdir->head) { | |
126 | + err = v9fs_file_readn(filp, rdir->buf, NULL, | |
127 | + buflen, filp->f_pos); | |
128 | + if (err <= 0) | |
129 | + goto unlock_and_exit; | |
130 | + | |
131 | + rdir->head = 0; | |
132 | + rdir->tail = err; | |
133 | + } | |
134 | + | |
135 | + while (rdir->head < rdir->tail) { | |
136 | + err = p9stat_read(rdir->buf + rdir->head, | |
137 | + buflen - rdir->head, &st, | |
138 | + fid->clnt->dotu); | |
98 | 139 | if (err) { |
99 | 140 | P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); |
100 | 141 | err = -EIO; |
101 | 142 | p9stat_free(&st); |
102 | - goto free_and_exit; | |
143 | + goto unlock_and_exit; | |
103 | 144 | } |
145 | + reclen = st.size+2; | |
104 | 146 | |
105 | - i += st.size+2; | |
106 | - fid->rdir_fpos += st.size+2; | |
107 | - | |
108 | 147 | over = filldir(dirent, st.name, strlen(st.name), |
109 | 148 | filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st)); |
110 | 149 | |
111 | - filp->f_pos += st.size+2; | |
112 | - | |
113 | 150 | p9stat_free(&st); |
114 | 151 | |
115 | 152 | if (over) { |
116 | 153 | err = 0; |
117 | - goto free_and_exit; | |
154 | + goto unlock_and_exit; | |
118 | 155 | } |
156 | + rdir->head += reclen; | |
157 | + filp->f_pos += reclen; | |
119 | 158 | } |
120 | 159 | } |
121 | 160 | |
122 | -free_and_exit: | |
123 | - kfree(statbuf); | |
161 | +unlock_and_exit: | |
162 | + mutex_unlock(&rdir->mutex); | |
163 | +exit: | |
124 | 164 | return err; |
125 | 165 | } |
126 | 166 |
include/net/9p/client.h
... | ... | @@ -159,8 +159,7 @@ |
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 | * |
167 | 166 | |
... | ... | @@ -174,9 +173,9 @@ |
174 | 173 | struct p9_qid qid; |
175 | 174 | u32 iounit; |
176 | 175 | uid_t uid; |
177 | - void *aux; | |
178 | 176 | |
179 | - int rdir_fpos; | |
177 | + void *rdir; | |
178 | + | |
180 | 179 | struct list_head flist; |
181 | 180 | struct list_head dlist; /* list of all fids attached to a dentry */ |
182 | 181 | }; |
net/9p/client.c
... | ... | @@ -582,11 +582,9 @@ |
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); |
... | ... | @@ -609,6 +607,7 @@ |
609 | 607 | spin_lock_irqsave(&clnt->lock, flags); |
610 | 608 | list_del(&fid->flist); |
611 | 609 | spin_unlock_irqrestore(&clnt->lock, flags); |
610 | + kfree(fid->rdir); | |
612 | 611 | kfree(fid); |
613 | 612 | } |
614 | 613 |