diff options
Diffstat (limited to 'fs/ioctl.c')
| -rw-r--r-- | fs/ioctl.c | 118 |
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 | */ | ||
| 245 | int 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 | } | ||
| 343 | EXPORT_SYMBOL(generic_block_fiemap); | ||
| 344 | |||
| 227 | static int file_ioctl(struct file *filp, unsigned int cmd, | 345 | static int file_ioctl(struct file *filp, unsigned int cmd, |
| 228 | unsigned long arg) | 346 | unsigned long arg) |
| 229 | { | 347 | { |
