lseek(fd, addr, SEEK_DATA) adjust file offset to the start address of next VMA,
or to addr if this address is allocated.

lseek(fd, addr, SEEK_HOLE) adjust file offset to the end address of VMA which
addr belongs to, or to addr itself if there is hole.

This way SEEK_HOLE reports a virtual zero-length hole between each contiguous
VMAs. This hack seems completely legit and allows to simplify implementation
(there is no function for finding next hole in VMAs' tree, walking along
 ->vm_next might be expensive) This also gives more information about layout.

Signed-off-by: Konstantin Khlebnikov <koc...@gmail.com>

---

I have no practical use for this, just found this interesting.
---
 fs/proc/base.c |   31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 2d696b0..aba4b47 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -769,13 +769,40 @@ static ssize_t mem_write(struct file *file, const char 
__user *buf,
 
 loff_t mem_lseek(struct file *file, loff_t offset, int orig)
 {
+       struct mm_struct *mm = file->private_data;
+       struct vm_area_struct *vma;
+
        switch (orig) {
-       case 0:
+       case SEEK_SET:
                file->f_pos = offset;
                break;
-       case 1:
+       case SEEK_CUR:
                file->f_pos += offset;
                break;
+       case SEEK_DATA:
+       case SEEK_HOLE:
+               if (!mm || !atomic_inc_not_zero(&mm->mm_users))
+                       return -ENXIO;
+               down_read(&mm->mmap_sem);
+               vma = find_vma(mm, offset);
+               if (vma) {
+                       if (orig == SEEK_DATA) {
+                               if (offset >= vma->vm_start)
+                                       file->f_pos = offset;
+                               else
+                                       file->f_pos = vma->vm_start;
+                       } else {
+                               if (offset < vma->vm_start)
+                                       file->f_pos = offset;
+                               else
+                                       file->f_pos = vma->vm_end;
+                       }
+               }
+               up_read(&mm->mmap_sem);
+               mmput(mm);
+               if (!vma)
+                       return -ENXIO;
+               break;
        default:
                return -EINVAL;
        }

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to