btrfs: tree_search, copy_to_sk: return EOVERFLOW for too small buffer
authorGerhard Heift <gerhard@heift.name>
Thu, 30 Jan 2014 15:23:59 +0000 (16:23 +0100)
committerChris Mason <clm@fb.com>
Fri, 13 Jun 2014 01:21:39 +0000 (18:21 -0700)
In copy_to_sk, if an item is too large for the given buffer, it now returns
-EOVERFLOW instead of copying a search_header with len = 0. For backward
compatibility for the first item it still copies such a header to the buffer,
but not any other following items, which could have fitted.

tree_search changes -EOVERFLOW back to 0 to behave similiar to the way it
behaved before this patch.

Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
Signed-off-by: Chris Mason <clm@fb.com>
Acked-by: David Sterba <dsterba@suse.cz>
fs/btrfs/ioctl.c

index 7756404..6e09fc1 100644 (file)
@@ -1990,8 +1990,20 @@ static noinline int copy_to_sk(struct btrfs_root *root,
                if (!key_in_sk(key, sk))
                        continue;
 
-               if (sizeof(sh) + item_len > buf_size)
+               if (sizeof(sh) + item_len > buf_size) {
+                       if (*num_found) {
+                               ret = 1;
+                               goto out;
+                       }
+
+                       /*
+                        * return one empty item back for v1, which does not
+                        * handle -EOVERFLOW
+                        */
+
                        item_len = 0;
+                       ret = -EOVERFLOW;
+               }
 
                if (sizeof(sh) + item_len + *sk_offset > buf_size) {
                        ret = 1;
@@ -2017,6 +2029,9 @@ static noinline int copy_to_sk(struct btrfs_root *root,
                }
                (*num_found)++;
 
+               if (ret) /* -EOVERFLOW from above */
+                       goto out;
+
                if (*num_found >= sk->nr_items) {
                        ret = 1;
                        goto out;
@@ -2095,7 +2110,8 @@ static noinline int search_ioctl(struct inode *inode,
                        break;
 
        }
-       ret = 0;
+       if (ret > 0)
+               ret = 0;
 err:
        sk->nr_items = num_found;
        btrfs_free_path(path);
@@ -2118,6 +2134,14 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
 
        inode = file_inode(file);
        ret = search_ioctl(inode, &args->key, sizeof(args->buf), args->buf);
+
+       /*
+        * In the origin implementation an overflow is handled by returning a
+        * search header with a len of zero, so reset ret.
+        */
+       if (ret == -EOVERFLOW)
+               ret = 0;
+
        if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
                ret = -EFAULT;
        kfree(args);