aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ioctl.c')
-rw-r--r--fs/ioctl.c118
1 files changed, 118 insertions, 0 deletions
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 045d9601fbbd..33a6b7ecb8b8 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -13,6 +13,8 @@
13#include <linux/security.h> 13#include <linux/security.h>
14#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/uaccess.h> 15#include <linux/uaccess.h>
16#include <linux/writeback.h>
17#include <linux/buffer_head.h>
16 18
17#include <asm/ioctls.h> 19#include <asm/ioctls.h>
18 20
@@ -224,6 +226,122 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
224 return error; 226 return error;
225} 227}
226 228
229#define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits)
230#define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits);
231
232/*
233 * @inode - the inode to map
234 * @arg - the pointer to userspace where we copy everything to
235 * @get_block - the fs's get_block function
236 *
237 * This does FIEMAP for block based inodes. Basically it will just loop
238 * through get_block until we hit the number of extents we want to map, or we
239 * go past the end of the file and hit a hole.
240 *
241 * If it is possible to have data blocks beyond a hole past @inode->i_size, then
242 * please do not use this function, it will stop at the first unmapped block
243 * beyond i_size
244 */
245int generic_block_fiemap(struct inode *inode,
246 struct fiemap_extent_info *fieinfo, u64 start,
247 u64 len, get_block_t *get_block)
248{
249 struct buffer_head tmp;
250 unsigned int start_blk;
251 long long length = 0, map_len = 0;
252 u64 logical = 0, phys = 0, size = 0;
253 u32 flags = FIEMAP_EXTENT_MERGED;
254 int ret = 0;
255
256 if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC)))
257 return ret;
258
259 start_blk = logical_to_blk(inode, start);
260
261 /* guard against change */
262 mutex_lock(&inode->i_mutex);
263
264 length = (long long)min_t(u64, len, i_size_read(inode));
265 map_len = length;
266
267 do {
268 /*
269 * we set b_size to the total size we want so it will map as
270 * many contiguous blocks as possible at once
271 */
272 memset(&tmp, 0, sizeof(struct buffer_head));
273 tmp.b_size = map_len;
274
275 ret = get_block(inode, start_blk, &tmp, 0);
276 if (ret)
277 break;
278
279 /* HOLE */
280 if (!buffer_mapped(&tmp)) {
281 /*
282 * first hole after going past the EOF, this is our
283 * last extent
284 */
285 if (length <= 0) {
286 flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST;
287 ret = fiemap_fill_next_extent(fieinfo, logical,
288 phys, size,
289 flags);
290 break;
291 }
292
293 length -= blk_to_logical(inode, 1);
294
295 /* if we have holes up to/past EOF then we're done */
296 if (length <= 0)
297 break;
298
299 start_blk++;
300 } else {
301 if (length <= 0 && size) {
302 ret = fiemap_fill_next_extent(fieinfo, logical,
303 phys, size,
304 flags);
305 if (ret)
306 break;
307 }
308
309 logical = blk_to_logical(inode, start_blk);
310 phys = blk_to_logical(inode, tmp.b_blocknr);
311 size = tmp.b_size;
312 flags = FIEMAP_EXTENT_MERGED;
313
314 length -= tmp.b_size;
315 start_blk += logical_to_blk(inode, size);
316
317 /*
318 * if we are past the EOF we need to loop again to see
319 * if there is a hole so we can mark this extent as the
320 * last one, and if not keep mapping things until we
321 * find a hole, or we run out of slots in the extent
322 * array
323 */
324 if (length <= 0)
325 continue;
326
327 ret = fiemap_fill_next_extent(fieinfo, logical, phys,
328 size, flags);
329 if (ret)
330 break;
331 }
332 cond_resched();
333 } while (1);
334
335 mutex_unlock(&inode->i_mutex);
336
337 /* if ret is 1 then we just hit the end of the extent array */
338 if (ret == 1)
339 ret = 0;
340
341 return ret;
342}
343EXPORT_SYMBOL(generic_block_fiemap);
344
227static int file_ioctl(struct file *filp, unsigned int cmd, 345static int file_ioctl(struct file *filp, unsigned int cmd,
228 unsigned long arg) 346 unsigned long arg)
229{ 347{