ceph: fix ceph_dir_llseek()
authorYan, Zheng <zheng.z.yan@intel.com>
Thu, 27 Feb 2014 08:26:24 +0000 (16:26 +0800)
committerYan, Zheng <zheng.z.yan@intel.com>
Thu, 3 Apr 2014 02:33:52 +0000 (10:33 +0800)
Comparing offset with inode->i_sb->s_maxbytes doesn't make sense for
directory. For a fragmented directory, offset (frag_t, off) can be
larger than inode->i_sb->s_maxbytes.

At the very beginning of ceph_dir_llseek(), local variable old_offset
is initialized to parameter offset. This doesn't make sense neither.
Old_offset should be ceph_make_fpos(fi->frag, fi->next_offset).

Signed-off-by: Yan, Zheng <zheng.z.yan@intel.com>
Reviewed-by: Alex Elder <elder@linaro.org>
fs/ceph/dir.c
fs/ceph/super.h

index 45eda6d7a40c2030db42fc06fa2d741f180c254f..a7eaf9692aa6811092ff002e6e87f6e588c34bb2 100644 (file)
@@ -190,7 +190,7 @@ more:
                if (last) {
                        /* remember our position */
                        fi->dentry = last;
-                       fi->next_offset = di->offset;
+                       fi->next_offset = fpos_off(di->offset);
                }
                dput(dentry);
                return 0;
@@ -369,9 +369,9 @@ more:
                                fi->next_offset = 0;
                        off = fi->next_offset;
                }
+               fi->frag = frag;
                fi->offset = fi->next_offset;
                fi->last_readdir = req;
-               fi->frag = frag;
 
                if (req->r_reply_info.dir_end) {
                        kfree(fi->last_name);
@@ -474,7 +474,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
 {
        struct ceph_file_info *fi = file->private_data;
        struct inode *inode = file->f_mapping->host;
-       loff_t old_offset = offset;
+       loff_t old_offset = ceph_make_fpos(fi->frag, fi->next_offset);
        loff_t retval;
 
        mutex_lock(&inode->i_mutex);
@@ -491,7 +491,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
                goto out;
        }
 
-       if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {
+       if (offset >= 0) {
                if (offset != file->f_pos) {
                        file->f_pos = offset;
                        file->f_version = 0;
@@ -504,14 +504,14 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
                 * seek to new frag, or seek prior to current chunk.
                 */
                if (offset == 0 ||
-                   fpos_frag(offset) != fpos_frag(old_offset) ||
+                   fpos_frag(offset) != fi->frag ||
                    fpos_off(offset) < fi->offset) {
                        dout("dir_llseek dropping %p content\n", file);
                        reset_readdir(fi);
                }
 
                /* bump dir_release_count if we did a forward seek */
-               if (offset > old_offset)
+               if (fpos_cmp(offset, old_offset) > 0)
                        fi->dir_release_count--;
        }
 out:
index d8801a95b6857d514fc27e440af8d476513cae3e..70bb183385b755784fe6819d13970b4187bb32f3 100644 (file)
@@ -577,7 +577,7 @@ struct ceph_file_info {
 
        /* readdir: position within a frag */
        unsigned offset;       /* offset of last chunk, adjusted for . and .. */
-       u64 next_offset;       /* offset of next chunk (last_name's + 1) */
+       unsigned next_offset;  /* offset of next chunk (last_name's + 1) */
        char *last_name;       /* last entry in previous chunk */
        struct dentry *dentry; /* next dentry (for dcache readdir) */
        int dir_release_count;