diff options
Diffstat (limited to 'fs/9p/vfs_dir.c')
-rw-r--r-- | fs/9p/vfs_dir.c | 93 |
1 files changed, 67 insertions, 26 deletions
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 873cd31baa47..15cce53bf61e 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c | |||
@@ -40,6 +40,24 @@ | |||
40 | #include "fid.h" | 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 | * dt_type - return file type | 61 | * dt_type - return file type |
44 | * @mistat: mistat structure | 62 | * @mistat: mistat structure |
45 | * | 63 | * |
@@ -70,56 +88,79 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
70 | { | 88 | { |
71 | int over; | 89 | int over; |
72 | struct p9_wstat st; | 90 | struct p9_wstat st; |
73 | int err; | 91 | int err = 0; |
74 | struct p9_fid *fid; | 92 | struct p9_fid *fid; |
75 | int buflen; | 93 | int buflen; |
76 | char *statbuf; | 94 | int reclen = 0; |
77 | int n, i = 0; | 95 | struct p9_rdir *rdir; |
78 | 96 | ||
79 | P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); | 97 | P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); |
80 | fid = filp->private_data; | 98 | fid = filp->private_data; |
81 | 99 | ||
82 | buflen = fid->clnt->msize - P9_IOHDRSZ; | 100 | buflen = fid->clnt->msize - P9_IOHDRSZ; |
83 | statbuf = kmalloc(buflen, GFP_KERNEL); | 101 | |
84 | if (!statbuf) | 102 | /* allocate rdir on demand */ |
85 | return -ENOMEM; | 103 | if (!fid->rdir) { |
86 | 104 | rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); | |
87 | while (1) { | 105 | |
88 | err = v9fs_file_readn(filp, statbuf, NULL, buflen, | 106 | if (rdir == NULL) { |
89 | fid->rdir_fpos); | 107 | err = -ENOMEM; |
90 | if (err <= 0) | 108 | goto exit; |
91 | break; | 109 | } |
92 | 110 | spin_lock(&filp->f_dentry->d_lock); | |
93 | n = err; | 111 | if (!fid->rdir) { |
94 | while (i < n) { | 112 | rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir); |
95 | err = p9stat_read(statbuf + i, buflen-i, &st, | 113 | mutex_init(&rdir->mutex); |
96 | fid->clnt->dotu); | 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); | ||
97 | if (err) { | 139 | if (err) { |
98 | P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); | 140 | P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); |
99 | err = -EIO; | 141 | err = -EIO; |
100 | p9stat_free(&st); | 142 | p9stat_free(&st); |
101 | goto free_and_exit; | 143 | goto unlock_and_exit; |
102 | } | 144 | } |
103 | 145 | reclen = st.size+2; | |
104 | i += st.size+2; | ||
105 | fid->rdir_fpos += st.size+2; | ||
106 | 146 | ||
107 | over = filldir(dirent, st.name, strlen(st.name), | 147 | over = filldir(dirent, st.name, strlen(st.name), |
108 | filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st)); | 148 | filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st)); |
109 | 149 | ||
110 | filp->f_pos += st.size+2; | ||
111 | |||
112 | p9stat_free(&st); | 150 | p9stat_free(&st); |
113 | 151 | ||
114 | if (over) { | 152 | if (over) { |
115 | err = 0; | 153 | err = 0; |
116 | goto free_and_exit; | 154 | goto unlock_and_exit; |
117 | } | 155 | } |
156 | rdir->head += reclen; | ||
157 | filp->f_pos += reclen; | ||
118 | } | 158 | } |
119 | } | 159 | } |
120 | 160 | ||
121 | free_and_exit: | 161 | unlock_and_exit: |
122 | kfree(statbuf); | 162 | mutex_unlock(&rdir->mutex); |
163 | exit: | ||
123 | return err; | 164 | return err; |
124 | } | 165 | } |
125 | 166 | ||