diff options
author | Jaegeuk Kim <jaegeuk@kernel.org> | 2016-07-16 21:59:22 -0700 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2016-07-18 10:20:44 -0700 |
commit | 363cad7f7e586b385c20e9925b4923683d46deb6 (patch) | |
tree | 80a44b09f64c7b5a9a077d5d4397eaf358b6c688 /fs/f2fs | |
parent | dcf25fe8fcf4e68057d02e453e7ccf93fa1d1071 (diff) | |
download | lwn-363cad7f7e586b385c20e9925b4923683d46deb6.tar.gz lwn-363cad7f7e586b385c20e9925b4923683d46deb6.zip |
f2fs: avoid memory allocation failure due to a long length
We need to avoid ENOMEM due to unexpected long length.
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'fs/f2fs')
-rw-r--r-- | fs/f2fs/file.c | 46 |
1 files changed, 28 insertions, 18 deletions
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 47f1f5e36d1c..9f9cb6489997 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1000,33 +1000,43 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode, static int __exchange_data_block(struct inode *src_inode, struct inode *dst_inode, pgoff_t src, pgoff_t dst, - int len, bool full) + pgoff_t len, bool full) { block_t *src_blkaddr; int *do_replace; + pgoff_t olen; int ret; - src_blkaddr = f2fs_kvzalloc(sizeof(block_t) * len, GFP_KERNEL); - if (!src_blkaddr) - return -ENOMEM; + while (len) { + olen = min((pgoff_t)4 * ADDRS_PER_BLOCK, len); - do_replace = f2fs_kvzalloc(sizeof(int) * len, GFP_KERNEL); - if (!do_replace) { - kvfree(src_blkaddr); - return -ENOMEM; - } + src_blkaddr = f2fs_kvzalloc(sizeof(block_t) * olen, GFP_KERNEL); + if (!src_blkaddr) + return -ENOMEM; - ret = __read_out_blkaddrs(src_inode, src_blkaddr, do_replace, src, len); - if (ret) - goto roll_back; + do_replace = f2fs_kvzalloc(sizeof(int) * olen, GFP_KERNEL); + if (!do_replace) { + kvfree(src_blkaddr); + return -ENOMEM; + } - ret = __clone_blkaddrs(src_inode, dst_inode, src_blkaddr, - do_replace, src, dst, len, full); - if (ret) - goto roll_back; + ret = __read_out_blkaddrs(src_inode, src_blkaddr, + do_replace, src, olen); + if (ret) + goto roll_back; - kvfree(src_blkaddr); - kvfree(do_replace); + ret = __clone_blkaddrs(src_inode, dst_inode, src_blkaddr, + do_replace, src, dst, olen, full); + if (ret) + goto roll_back; + + src += olen; + dst += olen; + len -= olen; + + kvfree(src_blkaddr); + kvfree(do_replace); + } return 0; roll_back: |