aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ioctl.c
diff options
context:
space:
mode:
authorMark Fasheh <mfasheh@suse.com>2008-10-08 19:44:18 -0400
committerTheodore Ts'o <tytso@mit.edu>2008-10-08 19:44:18 -0400
commitc4b929b85bdb64afacbbf6453b1f2bf7e14c9e89 (patch)
tree6f5a4b1378d7b609d0f1ceab93fb501cf91b218a /fs/ioctl.c
parent4d20c685fa365766a8f13584b4c8178a15ab7103 (diff)
vfs: vfs-level fiemap interface
Basic vfs-level fiemap infrastructure, which sets up a new ->fiemap inode operation. Userspace can get extent information on a file via fiemap ioctl. As input, the fiemap ioctl takes a struct fiemap which includes an array of struct fiemap_extent (fm_extents). Size of the extent array is passed as fm_extent_count and number of extents returned will be written into fm_mapped_extents. Offset and length fields on the fiemap structure (fm_start, fm_length) describe a logical range which will be searched for extents. All extents returned will at least partially contain this range. The actual extent offsets and ranges returned will be unmodified from their offset and range on-disk. The fiemap ioctl returns '0' on success. On error, -1 is returned and errno is set. If errno is equal to EBADR, then fm_flags will contain those flags which were passed in which the kernel did not understand. On all other errors, the contents of fm_extents is undefined. As fiemap evolved, there have been many authors of the vfs patch. As far as I can tell, the list includes: Kalpak Shah <kalpak.shah@sun.com> Andreas Dilger <adilger@sun.com> Eric Sandeen <sandeen@redhat.com> Mark Fasheh <mfasheh@suse.com> Signed-off-by: Mark Fasheh <mfasheh@suse.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: Michael Kerrisk <mtk.manpages@googlemail.com> Cc: linux-api@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org
Diffstat (limited to 'fs/ioctl.c')
-rw-r--r--fs/ioctl.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 7db32b3382d3..045d9601fbbd 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -16,6 +16,9 @@
16 16
17#include <asm/ioctls.h> 17#include <asm/ioctls.h>
18 18
19/* So that the fiemap access checks can't overflow on 32 bit machines. */
20#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent))
21
19/** 22/**
20 * vfs_ioctl - call filesystem specific ioctl methods 23 * vfs_ioctl - call filesystem specific ioctl methods
21 * @filp: open file to invoke ioctl method on 24 * @filp: open file to invoke ioctl method on
@@ -71,6 +74,156 @@ static int ioctl_fibmap(struct file *filp, int __user *p)
71 return put_user(res, p); 74 return put_user(res, p);
72} 75}
73 76
77/**
78 * fiemap_fill_next_extent - Fiemap helper function
79 * @fieinfo: Fiemap context passed into ->fiemap
80 * @logical: Extent logical start offset, in bytes
81 * @phys: Extent physical start offset, in bytes
82 * @len: Extent length, in bytes
83 * @flags: FIEMAP_EXTENT flags that describe this extent
84 *
85 * Called from file system ->fiemap callback. Will populate extent
86 * info as passed in via arguments and copy to user memory. On
87 * success, extent count on fieinfo is incremented.
88 *
89 * Returns 0 on success, -errno on error, 1 if this was the last
90 * extent that will fit in user array.
91 */
92#define SET_UNKNOWN_FLAGS (FIEMAP_EXTENT_DELALLOC)
93#define SET_NO_UNMOUNTED_IO_FLAGS (FIEMAP_EXTENT_DATA_ENCRYPTED)
94#define SET_NOT_ALIGNED_FLAGS (FIEMAP_EXTENT_DATA_TAIL|FIEMAP_EXTENT_DATA_INLINE)
95int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
96 u64 phys, u64 len, u32 flags)
97{
98 struct fiemap_extent extent;
99 struct fiemap_extent *dest = fieinfo->fi_extents_start;
100
101 /* only count the extents */
102 if (fieinfo->fi_extents_max == 0) {
103 fieinfo->fi_extents_mapped++;
104 return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
105 }
106
107 if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
108 return 1;
109
110 if (flags & SET_UNKNOWN_FLAGS)
111 flags |= FIEMAP_EXTENT_UNKNOWN;
112 if (flags & SET_NO_UNMOUNTED_IO_FLAGS)
113 flags |= FIEMAP_EXTENT_ENCODED;
114 if (flags & SET_NOT_ALIGNED_FLAGS)
115 flags |= FIEMAP_EXTENT_NOT_ALIGNED;
116
117 memset(&extent, 0, sizeof(extent));
118 extent.fe_logical = logical;
119 extent.fe_physical = phys;
120 extent.fe_length = len;
121 extent.fe_flags = flags;
122
123 dest += fieinfo->fi_extents_mapped;
124 if (copy_to_user(dest, &extent, sizeof(extent)))
125 return -EFAULT;
126
127 fieinfo->fi_extents_mapped++;
128 if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
129 return 1;
130 return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
131}
132EXPORT_SYMBOL(fiemap_fill_next_extent);
133
134/**
135 * fiemap_check_flags - check validity of requested flags for fiemap
136 * @fieinfo: Fiemap context passed into ->fiemap
137 * @fs_flags: Set of fiemap flags that the file system understands
138 *
139 * Called from file system ->fiemap callback. This will compute the
140 * intersection of valid fiemap flags and those that the fs supports. That
141 * value is then compared against the user supplied flags. In case of bad user
142 * flags, the invalid values will be written into the fieinfo structure, and
143 * -EBADR is returned, which tells ioctl_fiemap() to return those values to
144 * userspace. For this reason, a return code of -EBADR should be preserved.
145 *
146 * Returns 0 on success, -EBADR on bad flags.
147 */
148int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags)
149{
150 u32 incompat_flags;
151
152 incompat_flags = fieinfo->fi_flags & ~(FIEMAP_FLAGS_COMPAT & fs_flags);
153 if (incompat_flags) {
154 fieinfo->fi_flags = incompat_flags;
155 return -EBADR;
156 }
157 return 0;
158}
159EXPORT_SYMBOL(fiemap_check_flags);
160
161static int fiemap_check_ranges(struct super_block *sb,
162 u64 start, u64 len, u64 *new_len)
163{
164 *new_len = len;
165
166 if (len == 0)
167 return -EINVAL;
168
169 if (start > sb->s_maxbytes)
170 return -EFBIG;
171
172 /*
173 * Shrink request scope to what the fs can actually handle.
174 */
175 if ((len > sb->s_maxbytes) ||
176 (sb->s_maxbytes - len) < start)
177 *new_len = sb->s_maxbytes - start;
178
179 return 0;
180}
181
182static int ioctl_fiemap(struct file *filp, unsigned long arg)
183{
184 struct fiemap fiemap;
185 struct fiemap_extent_info fieinfo = { 0, };
186 struct inode *inode = filp->f_path.dentry->d_inode;
187 struct super_block *sb = inode->i_sb;
188 u64 len;
189 int error;
190
191 if (!inode->i_op->fiemap)
192 return -EOPNOTSUPP;
193
194 if (copy_from_user(&fiemap, (struct fiemap __user *)arg,
195 sizeof(struct fiemap)))
196 return -EFAULT;
197
198 if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
199 return -EINVAL;
200
201 error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length,
202 &len);
203 if (error)
204 return error;
205
206 fieinfo.fi_flags = fiemap.fm_flags;
207 fieinfo.fi_extents_max = fiemap.fm_extent_count;
208 fieinfo.fi_extents_start = (struct fiemap_extent *)(arg + sizeof(fiemap));
209
210 if (fiemap.fm_extent_count != 0 &&
211 !access_ok(VERIFY_WRITE, fieinfo.fi_extents_start,
212 fieinfo.fi_extents_max * sizeof(struct fiemap_extent)))
213 return -EFAULT;
214
215 if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
216 filemap_write_and_wait(inode->i_mapping);
217
218 error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, len);
219 fiemap.fm_flags = fieinfo.fi_flags;
220 fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
221 if (copy_to_user((char *)arg, &fiemap, sizeof(fiemap)))
222 error = -EFAULT;
223
224 return error;
225}
226
74static int file_ioctl(struct file *filp, unsigned int cmd, 227static int file_ioctl(struct file *filp, unsigned int cmd,
75 unsigned long arg) 228 unsigned long arg)
76{ 229{
@@ -80,6 +233,8 @@ static int file_ioctl(struct file *filp, unsigned int cmd,
80 switch (cmd) { 233 switch (cmd) {
81 case FIBMAP: 234 case FIBMAP:
82 return ioctl_fibmap(filp, p); 235 return ioctl_fibmap(filp, p);
236 case FS_IOC_FIEMAP:
237 return ioctl_fiemap(filp, arg);
83 case FIGETBSZ: 238 case FIGETBSZ:
84 return put_user(inode->i_sb->s_blocksize, p); 239 return put_user(inode->i_sb->s_blocksize, p);
85 case FIONREAD: 240 case FIONREAD: