From: Jim G. <ga...@ll...> - 2009-09-28 17:34:16
|
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. Jim 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. Also, if multiple 9P read requests are required to satisfy the user's request, clear the 'i' index so that dirents returned in subsequent responses are not dropped, Finally, don't increment flip->f_pos if filldir fails. Index: v9fs/vfs_dir.c =================================================================== --- v9fs/vfs_dir.c (.../tags/v9fs-2009.08.27.9psac-3chaos) (revision 8998) +++ v9fs/vfs_dir.c (.../branches/9p-sac) (revision 8998) @@ -74,7 +74,7 @@ struct p9_fid *fid; int buflen; char *statbuf; - int n, i = 0; + int n, i, reclen; P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->COMPAT_f_dentry->d_name.name); fid = filp->private_data; @@ -85,12 +85,20 @@ return -ENOMEM; while (1) { - err = v9fs_file_readn(filp, statbuf, NULL, buflen, - fid->rdir_fpos); - if (err <= 0) - break; - - n = err; + if (fid->rdir_fpos > filp->f_pos) { + n = fid->rdir_fpos - filp->f_pos; + memcpy(statbuf, fid->aux, n); + kfree(fid->aux); + fid->aux = NULL; + } else { + err = v9fs_file_readn(filp, statbuf, NULL, buflen, + fid->rdir_fpos); + if (err <= 0) + break; + n = err; + fid->rdir_fpos += n; + } + i = 0; while (i < n) { err = p9stat_read(statbuf + i, buflen-i, &st, fid->clnt->dotu); @@ -100,21 +108,25 @@ p9stat_free(&st); goto free_and_exit; } + reclen = st.size+2; - i += st.size+2; - fid->rdir_fpos += st.size+2; - over = filldir(dirent, st.name, strlen(st.name), filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st)); - filp->f_pos += st.size+2; - p9stat_free(&st); if (over) { + fid->aux = kmalloc(n - i, GFP_KERNEL); + if (!fid->aux) { + err = -ENOMEM; + goto free_and_exit; + } + memcpy(fid->aux, statbuf + i, n - i); err = 0; goto free_and_exit; } + filp->f_pos += reclen; + i += reclen; } } Index: 9p/client.c =================================================================== --- 9p/client.c (.../tags/v9fs-2009.08.27.9psac-3chaos) (revision 8995) +++ 9p/client.c (.../branches/9p-sac) (revision 8995) @@ -615,6 +615,8 @@ spin_lock_irqsave(&clnt->lock, flags); list_del(&fid->flist); spin_unlock_irqrestore(&clnt->lock, flags); + if (fid->aux) + kfree(fid->aux); kfree(fid); } |