Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: fs: fix address space warnings in ioctl_fiemap() aio: check return value of create_workqueue() hpfs_setattr error case avoids unlock_kernel compat: copy missing fields in compat_statfs64 to user compat: update comment of compat statfs syscalls compat: remove unnecessary assignment in compat_rw_copy_check_uvector() fs: FS_POSIX_ACL does not depend on BLOCK fs: Remove unlikely() from fget_light() fs: Remove unlikely() from fput_light() fallocate should be a file operation make the feature checks in ->fallocate future proof staging: smbfs building fix tidy up around finish_automount() don't drop newmnt on error in do_add_mount() Take the completion of automount into new helper
This commit is contained in:
@@ -60,7 +60,6 @@ ata *);
|
|||||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||||
int (*removexattr) (struct dentry *, const char *);
|
int (*removexattr) (struct dentry *, const char *);
|
||||||
void (*truncate_range)(struct inode *, loff_t, loff_t);
|
void (*truncate_range)(struct inode *, loff_t, loff_t);
|
||||||
long (*fallocate)(struct inode *inode, int mode, loff_t offset, loff_t len);
|
|
||||||
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
|
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
|
||||||
|
|
||||||
locking rules:
|
locking rules:
|
||||||
@@ -88,7 +87,6 @@ getxattr: no
|
|||||||
listxattr: no
|
listxattr: no
|
||||||
removexattr: yes
|
removexattr: yes
|
||||||
truncate_range: yes
|
truncate_range: yes
|
||||||
fallocate: no
|
|
||||||
fiemap: no
|
fiemap: no
|
||||||
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
|
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
|
||||||
victim.
|
victim.
|
||||||
@@ -437,6 +435,7 @@ prototypes:
|
|||||||
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *,
|
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *,
|
||||||
size_t, unsigned int);
|
size_t, unsigned int);
|
||||||
int (*setlease)(struct file *, long, struct file_lock **);
|
int (*setlease)(struct file *, long, struct file_lock **);
|
||||||
|
long (*fallocate)(struct file *, int, loff_t, loff_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
locking rules:
|
locking rules:
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ static int smb_compare_dentry(const struct dentry *,
|
|||||||
unsigned int, const char *, const struct qstr *);
|
unsigned int, const char *, const struct qstr *);
|
||||||
static int smb_delete_dentry(const struct dentry *);
|
static int smb_delete_dentry(const struct dentry *);
|
||||||
|
|
||||||
static const struct dentry_operations smbfs_dentry_operations =
|
const struct dentry_operations smbfs_dentry_operations =
|
||||||
{
|
{
|
||||||
.d_revalidate = smb_lookup_validate,
|
.d_revalidate = smb_lookup_validate,
|
||||||
.d_hash = smb_hash_dentry,
|
.d_hash = smb_hash_dentry,
|
||||||
@@ -291,7 +291,7 @@ static const struct dentry_operations smbfs_dentry_operations =
|
|||||||
.d_delete = smb_delete_dentry,
|
.d_delete = smb_delete_dentry,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dentry_operations smbfs_dentry_operations_case =
|
const struct dentry_operations smbfs_dentry_operations_case =
|
||||||
{
|
{
|
||||||
.d_revalidate = smb_lookup_validate,
|
.d_revalidate = smb_lookup_validate,
|
||||||
.d_delete = smb_delete_dentry,
|
.d_delete = smb_delete_dentry,
|
||||||
|
|||||||
17
fs/Kconfig
17
fs/Kconfig
@@ -30,15 +30,6 @@ config FS_MBCACHE
|
|||||||
source "fs/reiserfs/Kconfig"
|
source "fs/reiserfs/Kconfig"
|
||||||
source "fs/jfs/Kconfig"
|
source "fs/jfs/Kconfig"
|
||||||
|
|
||||||
config FS_POSIX_ACL
|
|
||||||
# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs/nfs4)
|
|
||||||
#
|
|
||||||
# NOTE: you can implement Posix ACLs without these helpers (XFS does).
|
|
||||||
# Never use this symbol for ifdefs.
|
|
||||||
#
|
|
||||||
bool
|
|
||||||
default n
|
|
||||||
|
|
||||||
source "fs/xfs/Kconfig"
|
source "fs/xfs/Kconfig"
|
||||||
source "fs/gfs2/Kconfig"
|
source "fs/gfs2/Kconfig"
|
||||||
source "fs/ocfs2/Kconfig"
|
source "fs/ocfs2/Kconfig"
|
||||||
@@ -47,6 +38,14 @@ source "fs/nilfs2/Kconfig"
|
|||||||
|
|
||||||
endif # BLOCK
|
endif # BLOCK
|
||||||
|
|
||||||
|
# Posix ACL utility routines
|
||||||
|
#
|
||||||
|
# Note: Posix ACLs can be implemented without these helpers. Never use
|
||||||
|
# this symbol for ifdefs in core code.
|
||||||
|
#
|
||||||
|
config FS_POSIX_ACL
|
||||||
|
def_bool n
|
||||||
|
|
||||||
config EXPORTFS
|
config EXPORTFS
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
|||||||
2
fs/aio.c
2
fs/aio.c
@@ -87,7 +87,7 @@ static int __init aio_setup(void)
|
|||||||
|
|
||||||
aio_wq = create_workqueue("aio");
|
aio_wq = create_workqueue("aio");
|
||||||
abe_pool = mempool_create_kmalloc_pool(1, sizeof(struct aio_batch_entry));
|
abe_pool = mempool_create_kmalloc_pool(1, sizeof(struct aio_batch_entry));
|
||||||
BUG_ON(!abe_pool);
|
BUG_ON(!aio_wq || !abe_pool);
|
||||||
|
|
||||||
pr_debug("aio_setup: sizeof(struct page) = %d\n", (int)sizeof(struct page));
|
pr_debug("aio_setup: sizeof(struct page) = %d\n", (int)sizeof(struct page));
|
||||||
|
|
||||||
|
|||||||
113
fs/btrfs/file.c
113
fs/btrfs/file.c
@@ -24,6 +24,7 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/backing-dev.h>
|
#include <linux/backing-dev.h>
|
||||||
#include <linux/mpage.h>
|
#include <linux/mpage.h>
|
||||||
|
#include <linux/falloc.h>
|
||||||
#include <linux/swap.h>
|
#include <linux/swap.h>
|
||||||
#include <linux/writeback.h>
|
#include <linux/writeback.h>
|
||||||
#include <linux/statfs.h>
|
#include <linux/statfs.h>
|
||||||
@@ -1237,6 +1238,117 @@ static int btrfs_file_mmap(struct file *filp, struct vm_area_struct *vma)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long btrfs_fallocate(struct file *file, int mode,
|
||||||
|
loff_t offset, loff_t len)
|
||||||
|
{
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
struct extent_state *cached_state = NULL;
|
||||||
|
u64 cur_offset;
|
||||||
|
u64 last_byte;
|
||||||
|
u64 alloc_start;
|
||||||
|
u64 alloc_end;
|
||||||
|
u64 alloc_hint = 0;
|
||||||
|
u64 locked_end;
|
||||||
|
u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
|
||||||
|
struct extent_map *em;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
alloc_start = offset & ~mask;
|
||||||
|
alloc_end = (offset + len + mask) & ~mask;
|
||||||
|
|
||||||
|
/* We only support the FALLOC_FL_KEEP_SIZE mode */
|
||||||
|
if (mode & ~FALLOC_FL_KEEP_SIZE)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wait for ordered IO before we have any locks. We'll loop again
|
||||||
|
* below with the locks held.
|
||||||
|
*/
|
||||||
|
btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start);
|
||||||
|
|
||||||
|
mutex_lock(&inode->i_mutex);
|
||||||
|
ret = inode_newsize_ok(inode, alloc_end);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (alloc_start > inode->i_size) {
|
||||||
|
ret = btrfs_cont_expand(inode, alloc_start);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = btrfs_check_data_free_space(inode, alloc_end - alloc_start);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
locked_end = alloc_end - 1;
|
||||||
|
while (1) {
|
||||||
|
struct btrfs_ordered_extent *ordered;
|
||||||
|
|
||||||
|
/* the extent lock is ordered inside the running
|
||||||
|
* transaction
|
||||||
|
*/
|
||||||
|
lock_extent_bits(&BTRFS_I(inode)->io_tree, alloc_start,
|
||||||
|
locked_end, 0, &cached_state, GFP_NOFS);
|
||||||
|
ordered = btrfs_lookup_first_ordered_extent(inode,
|
||||||
|
alloc_end - 1);
|
||||||
|
if (ordered &&
|
||||||
|
ordered->file_offset + ordered->len > alloc_start &&
|
||||||
|
ordered->file_offset < alloc_end) {
|
||||||
|
btrfs_put_ordered_extent(ordered);
|
||||||
|
unlock_extent_cached(&BTRFS_I(inode)->io_tree,
|
||||||
|
alloc_start, locked_end,
|
||||||
|
&cached_state, GFP_NOFS);
|
||||||
|
/*
|
||||||
|
* we can't wait on the range with the transaction
|
||||||
|
* running or with the extent lock held
|
||||||
|
*/
|
||||||
|
btrfs_wait_ordered_range(inode, alloc_start,
|
||||||
|
alloc_end - alloc_start);
|
||||||
|
} else {
|
||||||
|
if (ordered)
|
||||||
|
btrfs_put_ordered_extent(ordered);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_offset = alloc_start;
|
||||||
|
while (1) {
|
||||||
|
em = btrfs_get_extent(inode, NULL, 0, cur_offset,
|
||||||
|
alloc_end - cur_offset, 0);
|
||||||
|
BUG_ON(IS_ERR(em) || !em);
|
||||||
|
last_byte = min(extent_map_end(em), alloc_end);
|
||||||
|
last_byte = (last_byte + mask) & ~mask;
|
||||||
|
if (em->block_start == EXTENT_MAP_HOLE ||
|
||||||
|
(cur_offset >= inode->i_size &&
|
||||||
|
!test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) {
|
||||||
|
ret = btrfs_prealloc_file_range(inode, mode, cur_offset,
|
||||||
|
last_byte - cur_offset,
|
||||||
|
1 << inode->i_blkbits,
|
||||||
|
offset + len,
|
||||||
|
&alloc_hint);
|
||||||
|
if (ret < 0) {
|
||||||
|
free_extent_map(em);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free_extent_map(em);
|
||||||
|
|
||||||
|
cur_offset = last_byte;
|
||||||
|
if (cur_offset >= alloc_end) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlock_extent_cached(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
|
||||||
|
&cached_state, GFP_NOFS);
|
||||||
|
|
||||||
|
btrfs_free_reserved_data_space(inode, alloc_end - alloc_start);
|
||||||
|
out:
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
const struct file_operations btrfs_file_operations = {
|
const struct file_operations btrfs_file_operations = {
|
||||||
.llseek = generic_file_llseek,
|
.llseek = generic_file_llseek,
|
||||||
.read = do_sync_read,
|
.read = do_sync_read,
|
||||||
@@ -1248,6 +1360,7 @@ const struct file_operations btrfs_file_operations = {
|
|||||||
.open = generic_file_open,
|
.open = generic_file_open,
|
||||||
.release = btrfs_release_file,
|
.release = btrfs_release_file,
|
||||||
.fsync = btrfs_sync_file,
|
.fsync = btrfs_sync_file,
|
||||||
|
.fallocate = btrfs_fallocate,
|
||||||
.unlocked_ioctl = btrfs_ioctl,
|
.unlocked_ioctl = btrfs_ioctl,
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
.compat_ioctl = btrfs_ioctl,
|
.compat_ioctl = btrfs_ioctl,
|
||||||
|
|||||||
111
fs/btrfs/inode.c
111
fs/btrfs/inode.c
@@ -7098,116 +7098,6 @@ int btrfs_prealloc_file_range_trans(struct inode *inode,
|
|||||||
min_size, actual_len, alloc_hint, trans);
|
min_size, actual_len, alloc_hint, trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long btrfs_fallocate(struct inode *inode, int mode,
|
|
||||||
loff_t offset, loff_t len)
|
|
||||||
{
|
|
||||||
struct extent_state *cached_state = NULL;
|
|
||||||
u64 cur_offset;
|
|
||||||
u64 last_byte;
|
|
||||||
u64 alloc_start;
|
|
||||||
u64 alloc_end;
|
|
||||||
u64 alloc_hint = 0;
|
|
||||||
u64 locked_end;
|
|
||||||
u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
|
|
||||||
struct extent_map *em;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
alloc_start = offset & ~mask;
|
|
||||||
alloc_end = (offset + len + mask) & ~mask;
|
|
||||||
|
|
||||||
/* We only support the FALLOC_FL_KEEP_SIZE mode */
|
|
||||||
if (mode && (mode != FALLOC_FL_KEEP_SIZE))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* wait for ordered IO before we have any locks. We'll loop again
|
|
||||||
* below with the locks held.
|
|
||||||
*/
|
|
||||||
btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start);
|
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
|
||||||
ret = inode_newsize_ok(inode, alloc_end);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (alloc_start > inode->i_size) {
|
|
||||||
ret = btrfs_cont_expand(inode, alloc_start);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = btrfs_check_data_free_space(inode, alloc_end - alloc_start);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
locked_end = alloc_end - 1;
|
|
||||||
while (1) {
|
|
||||||
struct btrfs_ordered_extent *ordered;
|
|
||||||
|
|
||||||
/* the extent lock is ordered inside the running
|
|
||||||
* transaction
|
|
||||||
*/
|
|
||||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, alloc_start,
|
|
||||||
locked_end, 0, &cached_state, GFP_NOFS);
|
|
||||||
ordered = btrfs_lookup_first_ordered_extent(inode,
|
|
||||||
alloc_end - 1);
|
|
||||||
if (ordered &&
|
|
||||||
ordered->file_offset + ordered->len > alloc_start &&
|
|
||||||
ordered->file_offset < alloc_end) {
|
|
||||||
btrfs_put_ordered_extent(ordered);
|
|
||||||
unlock_extent_cached(&BTRFS_I(inode)->io_tree,
|
|
||||||
alloc_start, locked_end,
|
|
||||||
&cached_state, GFP_NOFS);
|
|
||||||
/*
|
|
||||||
* we can't wait on the range with the transaction
|
|
||||||
* running or with the extent lock held
|
|
||||||
*/
|
|
||||||
btrfs_wait_ordered_range(inode, alloc_start,
|
|
||||||
alloc_end - alloc_start);
|
|
||||||
} else {
|
|
||||||
if (ordered)
|
|
||||||
btrfs_put_ordered_extent(ordered);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_offset = alloc_start;
|
|
||||||
while (1) {
|
|
||||||
em = btrfs_get_extent(inode, NULL, 0, cur_offset,
|
|
||||||
alloc_end - cur_offset, 0);
|
|
||||||
BUG_ON(IS_ERR(em) || !em);
|
|
||||||
last_byte = min(extent_map_end(em), alloc_end);
|
|
||||||
last_byte = (last_byte + mask) & ~mask;
|
|
||||||
if (em->block_start == EXTENT_MAP_HOLE ||
|
|
||||||
(cur_offset >= inode->i_size &&
|
|
||||||
!test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) {
|
|
||||||
ret = btrfs_prealloc_file_range(inode, mode, cur_offset,
|
|
||||||
last_byte - cur_offset,
|
|
||||||
1 << inode->i_blkbits,
|
|
||||||
offset + len,
|
|
||||||
&alloc_hint);
|
|
||||||
if (ret < 0) {
|
|
||||||
free_extent_map(em);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free_extent_map(em);
|
|
||||||
|
|
||||||
cur_offset = last_byte;
|
|
||||||
if (cur_offset >= alloc_end) {
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unlock_extent_cached(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
|
|
||||||
&cached_state, GFP_NOFS);
|
|
||||||
|
|
||||||
btrfs_free_reserved_data_space(inode, alloc_end - alloc_start);
|
|
||||||
out:
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int btrfs_set_page_dirty(struct page *page)
|
static int btrfs_set_page_dirty(struct page *page)
|
||||||
{
|
{
|
||||||
return __set_page_dirty_nobuffers(page);
|
return __set_page_dirty_nobuffers(page);
|
||||||
@@ -7310,7 +7200,6 @@ static const struct inode_operations btrfs_file_inode_operations = {
|
|||||||
.listxattr = btrfs_listxattr,
|
.listxattr = btrfs_listxattr,
|
||||||
.removexattr = btrfs_removexattr,
|
.removexattr = btrfs_removexattr,
|
||||||
.permission = btrfs_permission,
|
.permission = btrfs_permission,
|
||||||
.fallocate = btrfs_fallocate,
|
|
||||||
.fiemap = btrfs_fiemap,
|
.fiemap = btrfs_fiemap,
|
||||||
};
|
};
|
||||||
static const struct inode_operations btrfs_special_inode_operations = {
|
static const struct inode_operations btrfs_special_inode_operations = {
|
||||||
|
|||||||
10
fs/compat.c
10
fs/compat.c
@@ -257,7 +257,7 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following statfs calls are copies of code from fs/open.c and
|
* The following statfs calls are copies of code from fs/statfs.c and
|
||||||
* should be checked against those from time to time
|
* should be checked against those from time to time
|
||||||
*/
|
*/
|
||||||
asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf)
|
asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf)
|
||||||
@@ -320,7 +320,9 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat
|
|||||||
__put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
|
__put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
|
||||||
__put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
|
__put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
|
||||||
__put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
|
__put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
|
||||||
__put_user(kbuf->f_frsize, &ubuf->f_frsize))
|
__put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
|
||||||
|
__put_user(kbuf->f_flags, &ubuf->f_flags) ||
|
||||||
|
__clear_user(ubuf->f_spare, sizeof(ubuf->f_spare)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -597,10 +599,8 @@ ssize_t compat_rw_copy_check_uvector(int type,
|
|||||||
if (nr_segs > fast_segs) {
|
if (nr_segs > fast_segs) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
|
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
|
||||||
if (iov == NULL) {
|
if (iov == NULL)
|
||||||
*ret_pointer = fast_pointer;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*ret_pointer = iov;
|
*ret_pointer = iov;
|
||||||
|
|
||||||
|
|||||||
@@ -2065,7 +2065,7 @@ extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
|
|||||||
extern void ext4_ext_truncate(struct inode *);
|
extern void ext4_ext_truncate(struct inode *);
|
||||||
extern void ext4_ext_init(struct super_block *);
|
extern void ext4_ext_init(struct super_block *);
|
||||||
extern void ext4_ext_release(struct super_block *);
|
extern void ext4_ext_release(struct super_block *);
|
||||||
extern long ext4_fallocate(struct inode *inode, int mode, loff_t offset,
|
extern long ext4_fallocate(struct file *file, int mode, loff_t offset,
|
||||||
loff_t len);
|
loff_t len);
|
||||||
extern int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
|
extern int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
|
||||||
ssize_t len);
|
ssize_t len);
|
||||||
|
|||||||
@@ -3627,14 +3627,15 @@ static void ext4_falloc_update_inode(struct inode *inode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* preallocate space for a file. This implements ext4's fallocate inode
|
* preallocate space for a file. This implements ext4's fallocate file
|
||||||
* operation, which gets called from sys_fallocate system call.
|
* operation, which gets called from sys_fallocate system call.
|
||||||
* For block-mapped files, posix_fallocate should fall back to the method
|
* For block-mapped files, posix_fallocate should fall back to the method
|
||||||
* of writing zeroes to the required new blocks (the same behavior which is
|
* of writing zeroes to the required new blocks (the same behavior which is
|
||||||
* expected for file systems which do not support fallocate() system call).
|
* expected for file systems which do not support fallocate() system call).
|
||||||
*/
|
*/
|
||||||
long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len)
|
long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
||||||
{
|
{
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
handle_t *handle;
|
handle_t *handle;
|
||||||
loff_t new_size;
|
loff_t new_size;
|
||||||
unsigned int max_blocks;
|
unsigned int max_blocks;
|
||||||
@@ -3645,7 +3646,7 @@ long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len)
|
|||||||
unsigned int credits, blkbits = inode->i_blkbits;
|
unsigned int credits, blkbits = inode->i_blkbits;
|
||||||
|
|
||||||
/* We only support the FALLOC_FL_KEEP_SIZE mode */
|
/* We only support the FALLOC_FL_KEEP_SIZE mode */
|
||||||
if (mode && (mode != FALLOC_FL_KEEP_SIZE))
|
if (mode & ~FALLOC_FL_KEEP_SIZE)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3655,10 +3656,6 @@ long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len)
|
|||||||
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
|
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
/* preallocation to directories is currently not supported */
|
|
||||||
if (S_ISDIR(inode->i_mode))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
map.m_lblk = offset >> blkbits;
|
map.m_lblk = offset >> blkbits;
|
||||||
/*
|
/*
|
||||||
* We can't just convert len to max_blocks because
|
* We can't just convert len to max_blocks because
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ const struct file_operations ext4_file_operations = {
|
|||||||
.fsync = ext4_sync_file,
|
.fsync = ext4_sync_file,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = generic_file_splice_write,
|
||||||
|
.fallocate = ext4_fallocate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations ext4_file_inode_operations = {
|
const struct inode_operations ext4_file_inode_operations = {
|
||||||
@@ -223,7 +224,6 @@ const struct inode_operations ext4_file_inode_operations = {
|
|||||||
.removexattr = generic_removexattr,
|
.removexattr = generic_removexattr,
|
||||||
#endif
|
#endif
|
||||||
.check_acl = ext4_check_acl,
|
.check_acl = ext4_check_acl,
|
||||||
.fallocate = ext4_fallocate,
|
|
||||||
.fiemap = ext4_fiemap,
|
.fiemap = ext4_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ struct file *fget_light(unsigned int fd, int *fput_needed)
|
|||||||
struct files_struct *files = current->files;
|
struct files_struct *files = current->files;
|
||||||
|
|
||||||
*fput_needed = 0;
|
*fput_needed = 0;
|
||||||
if (likely((atomic_read(&files->count) == 1))) {
|
if (atomic_read(&files->count) == 1) {
|
||||||
file = fcheck_files(files, fd);
|
file = fcheck_files(files, fd);
|
||||||
} else {
|
} else {
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|||||||
258
fs/gfs2/file.c
258
fs/gfs2/file.c
@@ -19,6 +19,8 @@
|
|||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/gfs2_ondisk.h>
|
#include <linux/gfs2_ondisk.h>
|
||||||
#include <linux/ext2_fs.h>
|
#include <linux/ext2_fs.h>
|
||||||
|
#include <linux/falloc.h>
|
||||||
|
#include <linux/swap.h>
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
#include <linux/writeback.h>
|
#include <linux/writeback.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
@@ -610,6 +612,260 @@ static ssize_t gfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
return generic_file_aio_write(iocb, iov, nr_segs, pos);
|
return generic_file_aio_write(iocb, iov, nr_segs, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void empty_write_end(struct page *page, unsigned from,
|
||||||
|
unsigned to)
|
||||||
|
{
|
||||||
|
struct gfs2_inode *ip = GFS2_I(page->mapping->host);
|
||||||
|
|
||||||
|
page_zero_new_buffers(page, from, to);
|
||||||
|
flush_dcache_page(page);
|
||||||
|
mark_page_accessed(page);
|
||||||
|
|
||||||
|
if (!gfs2_is_writeback(ip))
|
||||||
|
gfs2_page_add_databufs(ip, page, from, to);
|
||||||
|
|
||||||
|
block_commit_write(page, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_empty_blocks(struct page *page, unsigned from, unsigned to)
|
||||||
|
{
|
||||||
|
unsigned start, end, next;
|
||||||
|
struct buffer_head *bh, *head;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!page_has_buffers(page)) {
|
||||||
|
error = __block_write_begin(page, from, to - from, gfs2_block_map);
|
||||||
|
if (unlikely(error))
|
||||||
|
return error;
|
||||||
|
|
||||||
|
empty_write_end(page, from, to);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bh = head = page_buffers(page);
|
||||||
|
next = end = 0;
|
||||||
|
while (next < from) {
|
||||||
|
next += bh->b_size;
|
||||||
|
bh = bh->b_this_page;
|
||||||
|
}
|
||||||
|
start = next;
|
||||||
|
do {
|
||||||
|
next += bh->b_size;
|
||||||
|
if (buffer_mapped(bh)) {
|
||||||
|
if (end) {
|
||||||
|
error = __block_write_begin(page, start, end - start,
|
||||||
|
gfs2_block_map);
|
||||||
|
if (unlikely(error))
|
||||||
|
return error;
|
||||||
|
empty_write_end(page, start, end);
|
||||||
|
end = 0;
|
||||||
|
}
|
||||||
|
start = next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
end = next;
|
||||||
|
bh = bh->b_this_page;
|
||||||
|
} while (next < to);
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
error = __block_write_begin(page, start, end - start, gfs2_block_map);
|
||||||
|
if (unlikely(error))
|
||||||
|
return error;
|
||||||
|
empty_write_end(page, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
|
||||||
|
int mode)
|
||||||
|
{
|
||||||
|
struct gfs2_inode *ip = GFS2_I(inode);
|
||||||
|
struct buffer_head *dibh;
|
||||||
|
int error;
|
||||||
|
u64 start = offset >> PAGE_CACHE_SHIFT;
|
||||||
|
unsigned int start_offset = offset & ~PAGE_CACHE_MASK;
|
||||||
|
u64 end = (offset + len - 1) >> PAGE_CACHE_SHIFT;
|
||||||
|
pgoff_t curr;
|
||||||
|
struct page *page;
|
||||||
|
unsigned int end_offset = (offset + len) & ~PAGE_CACHE_MASK;
|
||||||
|
unsigned int from, to;
|
||||||
|
|
||||||
|
if (!end_offset)
|
||||||
|
end_offset = PAGE_CACHE_SIZE;
|
||||||
|
|
||||||
|
error = gfs2_meta_inode_buffer(ip, &dibh);
|
||||||
|
if (unlikely(error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
|
||||||
|
|
||||||
|
if (gfs2_is_stuffed(ip)) {
|
||||||
|
error = gfs2_unstuff_dinode(ip, NULL);
|
||||||
|
if (unlikely(error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
curr = start;
|
||||||
|
offset = start << PAGE_CACHE_SHIFT;
|
||||||
|
from = start_offset;
|
||||||
|
to = PAGE_CACHE_SIZE;
|
||||||
|
while (curr <= end) {
|
||||||
|
page = grab_cache_page_write_begin(inode->i_mapping, curr,
|
||||||
|
AOP_FLAG_NOFS);
|
||||||
|
if (unlikely(!page)) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr == end)
|
||||||
|
to = end_offset;
|
||||||
|
error = write_empty_blocks(page, from, to);
|
||||||
|
if (!error && offset + to > inode->i_size &&
|
||||||
|
!(mode & FALLOC_FL_KEEP_SIZE)) {
|
||||||
|
i_size_write(inode, offset + to);
|
||||||
|
}
|
||||||
|
unlock_page(page);
|
||||||
|
page_cache_release(page);
|
||||||
|
if (error)
|
||||||
|
goto out;
|
||||||
|
curr++;
|
||||||
|
offset += PAGE_CACHE_SIZE;
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gfs2_dinode_out(ip, dibh->b_data);
|
||||||
|
mark_inode_dirty(inode);
|
||||||
|
|
||||||
|
brelse(dibh);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calc_max_reserv(struct gfs2_inode *ip, loff_t max, loff_t *len,
|
||||||
|
unsigned int *data_blocks, unsigned int *ind_blocks)
|
||||||
|
{
|
||||||
|
const struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||||
|
unsigned int max_blocks = ip->i_alloc->al_rgd->rd_free_clone;
|
||||||
|
unsigned int tmp, max_data = max_blocks - 3 * (sdp->sd_max_height - 1);
|
||||||
|
|
||||||
|
for (tmp = max_data; tmp > sdp->sd_diptrs;) {
|
||||||
|
tmp = DIV_ROUND_UP(tmp, sdp->sd_inptrs);
|
||||||
|
max_data -= tmp;
|
||||||
|
}
|
||||||
|
/* This calculation isn't the exact reverse of gfs2_write_calc_reserve,
|
||||||
|
so it might end up with fewer data blocks */
|
||||||
|
if (max_data <= *data_blocks)
|
||||||
|
return;
|
||||||
|
*data_blocks = max_data;
|
||||||
|
*ind_blocks = max_blocks - max_data;
|
||||||
|
*len = ((loff_t)max_data - 3) << sdp->sd_sb.sb_bsize_shift;
|
||||||
|
if (*len > max) {
|
||||||
|
*len = max;
|
||||||
|
gfs2_write_calc_reserv(ip, max, data_blocks, ind_blocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
|
||||||
|
loff_t len)
|
||||||
|
{
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
struct gfs2_sbd *sdp = GFS2_SB(inode);
|
||||||
|
struct gfs2_inode *ip = GFS2_I(inode);
|
||||||
|
unsigned int data_blocks = 0, ind_blocks = 0, rblocks;
|
||||||
|
loff_t bytes, max_bytes;
|
||||||
|
struct gfs2_alloc *al;
|
||||||
|
int error;
|
||||||
|
loff_t next = (offset + len - 1) >> sdp->sd_sb.sb_bsize_shift;
|
||||||
|
next = (next + 1) << sdp->sd_sb.sb_bsize_shift;
|
||||||
|
|
||||||
|
/* We only support the FALLOC_FL_KEEP_SIZE mode */
|
||||||
|
if (mode & ~FALLOC_FL_KEEP_SIZE)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
offset = (offset >> sdp->sd_sb.sb_bsize_shift) <<
|
||||||
|
sdp->sd_sb.sb_bsize_shift;
|
||||||
|
|
||||||
|
len = next - offset;
|
||||||
|
bytes = sdp->sd_max_rg_data * sdp->sd_sb.sb_bsize / 2;
|
||||||
|
if (!bytes)
|
||||||
|
bytes = UINT_MAX;
|
||||||
|
|
||||||
|
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh);
|
||||||
|
error = gfs2_glock_nq(&ip->i_gh);
|
||||||
|
if (unlikely(error))
|
||||||
|
goto out_uninit;
|
||||||
|
|
||||||
|
if (!gfs2_write_alloc_required(ip, offset, len))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
if (len < bytes)
|
||||||
|
bytes = len;
|
||||||
|
al = gfs2_alloc_get(ip);
|
||||||
|
if (!al) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = gfs2_quota_lock_check(ip);
|
||||||
|
if (error)
|
||||||
|
goto out_alloc_put;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks);
|
||||||
|
|
||||||
|
al->al_requested = data_blocks + ind_blocks;
|
||||||
|
error = gfs2_inplace_reserve(ip);
|
||||||
|
if (error) {
|
||||||
|
if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) {
|
||||||
|
bytes >>= 1;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
goto out_qunlock;
|
||||||
|
}
|
||||||
|
max_bytes = bytes;
|
||||||
|
calc_max_reserv(ip, len, &max_bytes, &data_blocks, &ind_blocks);
|
||||||
|
al->al_requested = data_blocks + ind_blocks;
|
||||||
|
|
||||||
|
rblocks = RES_DINODE + ind_blocks + RES_STATFS + RES_QUOTA +
|
||||||
|
RES_RG_HDR + gfs2_rg_blocks(al);
|
||||||
|
if (gfs2_is_jdata(ip))
|
||||||
|
rblocks += data_blocks ? data_blocks : 1;
|
||||||
|
|
||||||
|
error = gfs2_trans_begin(sdp, rblocks,
|
||||||
|
PAGE_CACHE_SIZE/sdp->sd_sb.sb_bsize);
|
||||||
|
if (error)
|
||||||
|
goto out_trans_fail;
|
||||||
|
|
||||||
|
error = fallocate_chunk(inode, offset, max_bytes, mode);
|
||||||
|
gfs2_trans_end(sdp);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
goto out_trans_fail;
|
||||||
|
|
||||||
|
len -= max_bytes;
|
||||||
|
offset += max_bytes;
|
||||||
|
gfs2_inplace_release(ip);
|
||||||
|
gfs2_quota_unlock(ip);
|
||||||
|
gfs2_alloc_put(ip);
|
||||||
|
}
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
out_trans_fail:
|
||||||
|
gfs2_inplace_release(ip);
|
||||||
|
out_qunlock:
|
||||||
|
gfs2_quota_unlock(ip);
|
||||||
|
out_alloc_put:
|
||||||
|
gfs2_alloc_put(ip);
|
||||||
|
out_unlock:
|
||||||
|
gfs2_glock_dq(&ip->i_gh);
|
||||||
|
out_uninit:
|
||||||
|
gfs2_holder_uninit(&ip->i_gh);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_GFS2_FS_LOCKING_DLM
|
#ifdef CONFIG_GFS2_FS_LOCKING_DLM
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -765,6 +1021,7 @@ const struct file_operations gfs2_file_fops = {
|
|||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = generic_file_splice_write,
|
||||||
.setlease = gfs2_setlease,
|
.setlease = gfs2_setlease,
|
||||||
|
.fallocate = gfs2_fallocate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct file_operations gfs2_dir_fops = {
|
const struct file_operations gfs2_dir_fops = {
|
||||||
@@ -794,6 +1051,7 @@ const struct file_operations gfs2_file_fops_nolock = {
|
|||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = generic_file_splice_write,
|
||||||
.setlease = generic_setlease,
|
.setlease = generic_setlease,
|
||||||
|
.fallocate = gfs2_fallocate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct file_operations gfs2_dir_fops_nolock = {
|
const struct file_operations gfs2_dir_fops_nolock = {
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
#include <linux/gfs2_ondisk.h>
|
#include <linux/gfs2_ondisk.h>
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
#include <linux/fiemap.h>
|
#include <linux/fiemap.h>
|
||||||
#include <linux/swap.h>
|
|
||||||
#include <linux/falloc.h>
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
#include "gfs2.h"
|
#include "gfs2.h"
|
||||||
@@ -1257,261 +1255,6 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void empty_write_end(struct page *page, unsigned from,
|
|
||||||
unsigned to)
|
|
||||||
{
|
|
||||||
struct gfs2_inode *ip = GFS2_I(page->mapping->host);
|
|
||||||
|
|
||||||
page_zero_new_buffers(page, from, to);
|
|
||||||
flush_dcache_page(page);
|
|
||||||
mark_page_accessed(page);
|
|
||||||
|
|
||||||
if (!gfs2_is_writeback(ip))
|
|
||||||
gfs2_page_add_databufs(ip, page, from, to);
|
|
||||||
|
|
||||||
block_commit_write(page, from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int write_empty_blocks(struct page *page, unsigned from, unsigned to)
|
|
||||||
{
|
|
||||||
unsigned start, end, next;
|
|
||||||
struct buffer_head *bh, *head;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
if (!page_has_buffers(page)) {
|
|
||||||
error = __block_write_begin(page, from, to - from, gfs2_block_map);
|
|
||||||
if (unlikely(error))
|
|
||||||
return error;
|
|
||||||
|
|
||||||
empty_write_end(page, from, to);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bh = head = page_buffers(page);
|
|
||||||
next = end = 0;
|
|
||||||
while (next < from) {
|
|
||||||
next += bh->b_size;
|
|
||||||
bh = bh->b_this_page;
|
|
||||||
}
|
|
||||||
start = next;
|
|
||||||
do {
|
|
||||||
next += bh->b_size;
|
|
||||||
if (buffer_mapped(bh)) {
|
|
||||||
if (end) {
|
|
||||||
error = __block_write_begin(page, start, end - start,
|
|
||||||
gfs2_block_map);
|
|
||||||
if (unlikely(error))
|
|
||||||
return error;
|
|
||||||
empty_write_end(page, start, end);
|
|
||||||
end = 0;
|
|
||||||
}
|
|
||||||
start = next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
end = next;
|
|
||||||
bh = bh->b_this_page;
|
|
||||||
} while (next < to);
|
|
||||||
|
|
||||||
if (end) {
|
|
||||||
error = __block_write_begin(page, start, end - start, gfs2_block_map);
|
|
||||||
if (unlikely(error))
|
|
||||||
return error;
|
|
||||||
empty_write_end(page, start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
|
|
||||||
int mode)
|
|
||||||
{
|
|
||||||
struct gfs2_inode *ip = GFS2_I(inode);
|
|
||||||
struct buffer_head *dibh;
|
|
||||||
int error;
|
|
||||||
u64 start = offset >> PAGE_CACHE_SHIFT;
|
|
||||||
unsigned int start_offset = offset & ~PAGE_CACHE_MASK;
|
|
||||||
u64 end = (offset + len - 1) >> PAGE_CACHE_SHIFT;
|
|
||||||
pgoff_t curr;
|
|
||||||
struct page *page;
|
|
||||||
unsigned int end_offset = (offset + len) & ~PAGE_CACHE_MASK;
|
|
||||||
unsigned int from, to;
|
|
||||||
|
|
||||||
if (!end_offset)
|
|
||||||
end_offset = PAGE_CACHE_SIZE;
|
|
||||||
|
|
||||||
error = gfs2_meta_inode_buffer(ip, &dibh);
|
|
||||||
if (unlikely(error))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
|
|
||||||
|
|
||||||
if (gfs2_is_stuffed(ip)) {
|
|
||||||
error = gfs2_unstuff_dinode(ip, NULL);
|
|
||||||
if (unlikely(error))
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
curr = start;
|
|
||||||
offset = start << PAGE_CACHE_SHIFT;
|
|
||||||
from = start_offset;
|
|
||||||
to = PAGE_CACHE_SIZE;
|
|
||||||
while (curr <= end) {
|
|
||||||
page = grab_cache_page_write_begin(inode->i_mapping, curr,
|
|
||||||
AOP_FLAG_NOFS);
|
|
||||||
if (unlikely(!page)) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curr == end)
|
|
||||||
to = end_offset;
|
|
||||||
error = write_empty_blocks(page, from, to);
|
|
||||||
if (!error && offset + to > inode->i_size &&
|
|
||||||
!(mode & FALLOC_FL_KEEP_SIZE)) {
|
|
||||||
i_size_write(inode, offset + to);
|
|
||||||
}
|
|
||||||
unlock_page(page);
|
|
||||||
page_cache_release(page);
|
|
||||||
if (error)
|
|
||||||
goto out;
|
|
||||||
curr++;
|
|
||||||
offset += PAGE_CACHE_SIZE;
|
|
||||||
from = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfs2_dinode_out(ip, dibh->b_data);
|
|
||||||
mark_inode_dirty(inode);
|
|
||||||
|
|
||||||
brelse(dibh);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void calc_max_reserv(struct gfs2_inode *ip, loff_t max, loff_t *len,
|
|
||||||
unsigned int *data_blocks, unsigned int *ind_blocks)
|
|
||||||
{
|
|
||||||
const struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
|
||||||
unsigned int max_blocks = ip->i_alloc->al_rgd->rd_free_clone;
|
|
||||||
unsigned int tmp, max_data = max_blocks - 3 * (sdp->sd_max_height - 1);
|
|
||||||
|
|
||||||
for (tmp = max_data; tmp > sdp->sd_diptrs;) {
|
|
||||||
tmp = DIV_ROUND_UP(tmp, sdp->sd_inptrs);
|
|
||||||
max_data -= tmp;
|
|
||||||
}
|
|
||||||
/* This calculation isn't the exact reverse of gfs2_write_calc_reserve,
|
|
||||||
so it might end up with fewer data blocks */
|
|
||||||
if (max_data <= *data_blocks)
|
|
||||||
return;
|
|
||||||
*data_blocks = max_data;
|
|
||||||
*ind_blocks = max_blocks - max_data;
|
|
||||||
*len = ((loff_t)max_data - 3) << sdp->sd_sb.sb_bsize_shift;
|
|
||||||
if (*len > max) {
|
|
||||||
*len = max;
|
|
||||||
gfs2_write_calc_reserv(ip, max, data_blocks, ind_blocks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static long gfs2_fallocate(struct inode *inode, int mode, loff_t offset,
|
|
||||||
loff_t len)
|
|
||||||
{
|
|
||||||
struct gfs2_sbd *sdp = GFS2_SB(inode);
|
|
||||||
struct gfs2_inode *ip = GFS2_I(inode);
|
|
||||||
unsigned int data_blocks = 0, ind_blocks = 0, rblocks;
|
|
||||||
loff_t bytes, max_bytes;
|
|
||||||
struct gfs2_alloc *al;
|
|
||||||
int error;
|
|
||||||
loff_t next = (offset + len - 1) >> sdp->sd_sb.sb_bsize_shift;
|
|
||||||
next = (next + 1) << sdp->sd_sb.sb_bsize_shift;
|
|
||||||
|
|
||||||
/* We only support the FALLOC_FL_KEEP_SIZE mode */
|
|
||||||
if (mode && (mode != FALLOC_FL_KEEP_SIZE))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
offset = (offset >> sdp->sd_sb.sb_bsize_shift) <<
|
|
||||||
sdp->sd_sb.sb_bsize_shift;
|
|
||||||
|
|
||||||
len = next - offset;
|
|
||||||
bytes = sdp->sd_max_rg_data * sdp->sd_sb.sb_bsize / 2;
|
|
||||||
if (!bytes)
|
|
||||||
bytes = UINT_MAX;
|
|
||||||
|
|
||||||
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh);
|
|
||||||
error = gfs2_glock_nq(&ip->i_gh);
|
|
||||||
if (unlikely(error))
|
|
||||||
goto out_uninit;
|
|
||||||
|
|
||||||
if (!gfs2_write_alloc_required(ip, offset, len))
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
while (len > 0) {
|
|
||||||
if (len < bytes)
|
|
||||||
bytes = len;
|
|
||||||
al = gfs2_alloc_get(ip);
|
|
||||||
if (!al) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = gfs2_quota_lock_check(ip);
|
|
||||||
if (error)
|
|
||||||
goto out_alloc_put;
|
|
||||||
|
|
||||||
retry:
|
|
||||||
gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks);
|
|
||||||
|
|
||||||
al->al_requested = data_blocks + ind_blocks;
|
|
||||||
error = gfs2_inplace_reserve(ip);
|
|
||||||
if (error) {
|
|
||||||
if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) {
|
|
||||||
bytes >>= 1;
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
goto out_qunlock;
|
|
||||||
}
|
|
||||||
max_bytes = bytes;
|
|
||||||
calc_max_reserv(ip, len, &max_bytes, &data_blocks, &ind_blocks);
|
|
||||||
al->al_requested = data_blocks + ind_blocks;
|
|
||||||
|
|
||||||
rblocks = RES_DINODE + ind_blocks + RES_STATFS + RES_QUOTA +
|
|
||||||
RES_RG_HDR + gfs2_rg_blocks(al);
|
|
||||||
if (gfs2_is_jdata(ip))
|
|
||||||
rblocks += data_blocks ? data_blocks : 1;
|
|
||||||
|
|
||||||
error = gfs2_trans_begin(sdp, rblocks,
|
|
||||||
PAGE_CACHE_SIZE/sdp->sd_sb.sb_bsize);
|
|
||||||
if (error)
|
|
||||||
goto out_trans_fail;
|
|
||||||
|
|
||||||
error = fallocate_chunk(inode, offset, max_bytes, mode);
|
|
||||||
gfs2_trans_end(sdp);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
goto out_trans_fail;
|
|
||||||
|
|
||||||
len -= max_bytes;
|
|
||||||
offset += max_bytes;
|
|
||||||
gfs2_inplace_release(ip);
|
|
||||||
gfs2_quota_unlock(ip);
|
|
||||||
gfs2_alloc_put(ip);
|
|
||||||
}
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
out_trans_fail:
|
|
||||||
gfs2_inplace_release(ip);
|
|
||||||
out_qunlock:
|
|
||||||
gfs2_quota_unlock(ip);
|
|
||||||
out_alloc_put:
|
|
||||||
gfs2_alloc_put(ip);
|
|
||||||
out_unlock:
|
|
||||||
gfs2_glock_dq(&ip->i_gh);
|
|
||||||
out_uninit:
|
|
||||||
gfs2_holder_uninit(&ip->i_gh);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||||
u64 start, u64 len)
|
u64 start, u64 len)
|
||||||
{
|
{
|
||||||
@@ -1562,7 +1305,6 @@ const struct inode_operations gfs2_file_iops = {
|
|||||||
.getxattr = gfs2_getxattr,
|
.getxattr = gfs2_getxattr,
|
||||||
.listxattr = gfs2_listxattr,
|
.listxattr = gfs2_listxattr,
|
||||||
.removexattr = gfs2_removexattr,
|
.removexattr = gfs2_removexattr,
|
||||||
.fallocate = gfs2_fallocate,
|
|
||||||
.fiemap = gfs2_fiemap,
|
.fiemap = gfs2_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ int hpfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|||||||
attr->ia_size != i_size_read(inode)) {
|
attr->ia_size != i_size_read(inode)) {
|
||||||
error = vmtruncate(inode, attr->ia_size);
|
error = vmtruncate(inode, attr->ia_size);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
setattr_copy(inode, attr);
|
setattr_copy(inode, attr);
|
||||||
|
|||||||
@@ -70,8 +70,7 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
|
|||||||
extern void release_mounts(struct list_head *);
|
extern void release_mounts(struct list_head *);
|
||||||
extern void umount_tree(struct vfsmount *, int, struct list_head *);
|
extern void umount_tree(struct vfsmount *, int, struct list_head *);
|
||||||
extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
|
extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
|
||||||
extern int do_add_mount(struct vfsmount *, struct path *, int);
|
extern int finish_automount(struct vfsmount *, struct path *);
|
||||||
extern void mnt_clear_expiry(struct vfsmount *);
|
|
||||||
|
|
||||||
extern void mnt_make_longterm(struct vfsmount *);
|
extern void mnt_make_longterm(struct vfsmount *);
|
||||||
extern void mnt_make_shortterm(struct vfsmount *);
|
extern void mnt_make_shortterm(struct vfsmount *);
|
||||||
|
|||||||
10
fs/ioctl.c
10
fs/ioctl.c
@@ -86,7 +86,7 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
|
|||||||
u64 phys, u64 len, u32 flags)
|
u64 phys, u64 len, u32 flags)
|
||||||
{
|
{
|
||||||
struct fiemap_extent extent;
|
struct fiemap_extent extent;
|
||||||
struct fiemap_extent *dest = fieinfo->fi_extents_start;
|
struct fiemap_extent __user *dest = fieinfo->fi_extents_start;
|
||||||
|
|
||||||
/* only count the extents */
|
/* only count the extents */
|
||||||
if (fieinfo->fi_extents_max == 0) {
|
if (fieinfo->fi_extents_max == 0) {
|
||||||
@@ -173,6 +173,7 @@ static int fiemap_check_ranges(struct super_block *sb,
|
|||||||
static int ioctl_fiemap(struct file *filp, unsigned long arg)
|
static int ioctl_fiemap(struct file *filp, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct fiemap fiemap;
|
struct fiemap fiemap;
|
||||||
|
struct fiemap __user *ufiemap = (struct fiemap __user *) arg;
|
||||||
struct fiemap_extent_info fieinfo = { 0, };
|
struct fiemap_extent_info fieinfo = { 0, };
|
||||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
@@ -182,8 +183,7 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
|
|||||||
if (!inode->i_op->fiemap)
|
if (!inode->i_op->fiemap)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (copy_from_user(&fiemap, (struct fiemap __user *)arg,
|
if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))
|
||||||
sizeof(struct fiemap)))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
|
if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
|
||||||
@@ -196,7 +196,7 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
|
|||||||
|
|
||||||
fieinfo.fi_flags = fiemap.fm_flags;
|
fieinfo.fi_flags = fiemap.fm_flags;
|
||||||
fieinfo.fi_extents_max = fiemap.fm_extent_count;
|
fieinfo.fi_extents_max = fiemap.fm_extent_count;
|
||||||
fieinfo.fi_extents_start = (struct fiemap_extent *)(arg + sizeof(fiemap));
|
fieinfo.fi_extents_start = ufiemap->fm_extents;
|
||||||
|
|
||||||
if (fiemap.fm_extent_count != 0 &&
|
if (fiemap.fm_extent_count != 0 &&
|
||||||
!access_ok(VERIFY_WRITE, fieinfo.fi_extents_start,
|
!access_ok(VERIFY_WRITE, fieinfo.fi_extents_start,
|
||||||
@@ -209,7 +209,7 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
|
|||||||
error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, len);
|
error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, len);
|
||||||
fiemap.fm_flags = fieinfo.fi_flags;
|
fiemap.fm_flags = fieinfo.fi_flags;
|
||||||
fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
|
fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
|
||||||
if (copy_to_user((char *)arg, &fiemap, sizeof(fiemap)))
|
if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
|
||||||
error = -EFAULT;
|
error = -EFAULT;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
31
fs/namei.c
31
fs/namei.c
@@ -923,37 +923,13 @@ static int follow_automount(struct path *path, unsigned flags,
|
|||||||
if (!mnt) /* mount collision */
|
if (!mnt) /* mount collision */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* The new mount record should have at least 2 refs to prevent it being
|
err = finish_automount(mnt, path);
|
||||||
* expired before we get a chance to add it
|
|
||||||
*/
|
|
||||||
BUG_ON(mnt_get_count(mnt) < 2);
|
|
||||||
|
|
||||||
if (mnt->mnt_sb == path->mnt->mnt_sb &&
|
|
||||||
mnt->mnt_root == path->dentry) {
|
|
||||||
mnt_clear_expiry(mnt);
|
|
||||||
mntput(mnt);
|
|
||||||
mntput(mnt);
|
|
||||||
return -ELOOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to add the mountpoint to the parent. The filesystem may
|
|
||||||
* have placed it on an expiry list, and so we need to make sure it
|
|
||||||
* won't be expired under us if do_add_mount() fails (do_add_mount()
|
|
||||||
* will eat a reference unconditionally).
|
|
||||||
*/
|
|
||||||
mntget(mnt);
|
|
||||||
err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case -EBUSY:
|
case -EBUSY:
|
||||||
/* Someone else made a mount here whilst we were busy */
|
/* Someone else made a mount here whilst we were busy */
|
||||||
err = 0;
|
return 0;
|
||||||
default:
|
|
||||||
mnt_clear_expiry(mnt);
|
|
||||||
mntput(mnt);
|
|
||||||
mntput(mnt);
|
|
||||||
return err;
|
|
||||||
case 0:
|
case 0:
|
||||||
mntput(mnt);
|
|
||||||
dput(path->dentry);
|
dput(path->dentry);
|
||||||
if (*need_mntput)
|
if (*need_mntput)
|
||||||
mntput(path->mnt);
|
mntput(path->mnt);
|
||||||
@@ -961,7 +937,10 @@ static int follow_automount(struct path *path, unsigned flags,
|
|||||||
path->dentry = dget(mnt->mnt_root);
|
path->dentry = dget(mnt->mnt_root);
|
||||||
*need_mntput = true;
|
*need_mntput = true;
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1872,6 +1872,8 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_add_mount(struct vfsmount *, struct path *, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create a new mount for userspace and request it to be added into the
|
* create a new mount for userspace and request it to be added into the
|
||||||
* namespace's tree
|
* namespace's tree
|
||||||
@@ -1880,6 +1882,7 @@ static int do_new_mount(struct path *path, char *type, int flags,
|
|||||||
int mnt_flags, char *name, void *data)
|
int mnt_flags, char *name, void *data)
|
||||||
{
|
{
|
||||||
struct vfsmount *mnt;
|
struct vfsmount *mnt;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -1892,14 +1895,47 @@ static int do_new_mount(struct path *path, char *type, int flags,
|
|||||||
if (IS_ERR(mnt))
|
if (IS_ERR(mnt))
|
||||||
return PTR_ERR(mnt);
|
return PTR_ERR(mnt);
|
||||||
|
|
||||||
return do_add_mount(mnt, path, mnt_flags);
|
err = do_add_mount(mnt, path, mnt_flags);
|
||||||
|
if (err)
|
||||||
|
mntput(mnt);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int finish_automount(struct vfsmount *m, struct path *path)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
/* The new mount record should have at least 2 refs to prevent it being
|
||||||
|
* expired before we get a chance to add it
|
||||||
|
*/
|
||||||
|
BUG_ON(mnt_get_count(m) < 2);
|
||||||
|
|
||||||
|
if (m->mnt_sb == path->mnt->mnt_sb &&
|
||||||
|
m->mnt_root == path->dentry) {
|
||||||
|
err = -ELOOP;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = do_add_mount(m, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
|
||||||
|
if (!err)
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
/* remove m from any expiration list it may be on */
|
||||||
|
if (!list_empty(&m->mnt_expire)) {
|
||||||
|
down_write(&namespace_sem);
|
||||||
|
br_write_lock(vfsmount_lock);
|
||||||
|
list_del_init(&m->mnt_expire);
|
||||||
|
br_write_unlock(vfsmount_lock);
|
||||||
|
up_write(&namespace_sem);
|
||||||
|
}
|
||||||
|
mntput(m);
|
||||||
|
mntput(m);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* add a mount into a namespace's mount tree
|
* add a mount into a namespace's mount tree
|
||||||
* - this unconditionally eats one of the caller's references to newmnt.
|
|
||||||
*/
|
*/
|
||||||
int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
|
static int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@@ -1926,15 +1962,10 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
|
|||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
newmnt->mnt_flags = mnt_flags;
|
newmnt->mnt_flags = mnt_flags;
|
||||||
if ((err = graft_tree(newmnt, path)))
|
err = graft_tree(newmnt, path);
|
||||||
goto unlock;
|
|
||||||
|
|
||||||
up_write(&namespace_sem);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
up_write(&namespace_sem);
|
up_write(&namespace_sem);
|
||||||
mntput(newmnt);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1955,20 +1986,6 @@ void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mnt_set_expiry);
|
EXPORT_SYMBOL(mnt_set_expiry);
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove a vfsmount from any expiration list it may be on
|
|
||||||
*/
|
|
||||||
void mnt_clear_expiry(struct vfsmount *mnt)
|
|
||||||
{
|
|
||||||
if (!list_empty(&mnt->mnt_expire)) {
|
|
||||||
down_write(&namespace_sem);
|
|
||||||
br_write_lock(vfsmount_lock);
|
|
||||||
list_del_init(&mnt->mnt_expire);
|
|
||||||
br_write_unlock(vfsmount_lock);
|
|
||||||
up_write(&namespace_sem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* process a list of expirable mountpoints with the intent of discarding any
|
* process a list of expirable mountpoints with the intent of discarding any
|
||||||
* mountpoints that aren't in use and haven't been touched since last we came
|
* mountpoints that aren't in use and haven't been touched since last we came
|
||||||
|
|||||||
@@ -1989,20 +1989,20 @@ int ocfs2_change_file_space(struct file *file, unsigned int cmd,
|
|||||||
return __ocfs2_change_file_space(file, inode, file->f_pos, cmd, sr, 0);
|
return __ocfs2_change_file_space(file, inode, file->f_pos, cmd, sr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long ocfs2_fallocate(struct inode *inode, int mode, loff_t offset,
|
static long ocfs2_fallocate(struct file *file, int mode, loff_t offset,
|
||||||
loff_t len)
|
loff_t len)
|
||||||
{
|
{
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
||||||
struct ocfs2_space_resv sr;
|
struct ocfs2_space_resv sr;
|
||||||
int change_size = 1;
|
int change_size = 1;
|
||||||
int cmd = OCFS2_IOC_RESVSP64;
|
int cmd = OCFS2_IOC_RESVSP64;
|
||||||
|
|
||||||
|
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
if (!ocfs2_writes_unwritten_extents(osb))
|
if (!ocfs2_writes_unwritten_extents(osb))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (S_ISDIR(inode->i_mode))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
if (mode & FALLOC_FL_KEEP_SIZE)
|
if (mode & FALLOC_FL_KEEP_SIZE)
|
||||||
change_size = 0;
|
change_size = 0;
|
||||||
|
|
||||||
@@ -2610,7 +2610,6 @@ const struct inode_operations ocfs2_file_iops = {
|
|||||||
.getxattr = generic_getxattr,
|
.getxattr = generic_getxattr,
|
||||||
.listxattr = ocfs2_listxattr,
|
.listxattr = ocfs2_listxattr,
|
||||||
.removexattr = generic_removexattr,
|
.removexattr = generic_removexattr,
|
||||||
.fallocate = ocfs2_fallocate,
|
|
||||||
.fiemap = ocfs2_fiemap,
|
.fiemap = ocfs2_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2642,6 +2641,7 @@ const struct file_operations ocfs2_fops = {
|
|||||||
.flock = ocfs2_flock,
|
.flock = ocfs2_flock,
|
||||||
.splice_read = ocfs2_file_splice_read,
|
.splice_read = ocfs2_file_splice_read,
|
||||||
.splice_write = ocfs2_file_splice_write,
|
.splice_write = ocfs2_file_splice_write,
|
||||||
|
.fallocate = ocfs2_fallocate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct file_operations ocfs2_dops = {
|
const struct file_operations ocfs2_dops = {
|
||||||
|
|||||||
@@ -255,10 +255,10 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
|||||||
if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
|
if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
|
||||||
return -EFBIG;
|
return -EFBIG;
|
||||||
|
|
||||||
if (!inode->i_op->fallocate)
|
if (!file->f_op->fallocate)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
return inode->i_op->fallocate(inode, mode, offset, len);
|
return file->f_op->fallocate(file, mode, offset, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)
|
SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "xfs_trace.h"
|
#include "xfs_trace.h"
|
||||||
|
|
||||||
#include <linux/dcache.h>
|
#include <linux/dcache.h>
|
||||||
|
#include <linux/falloc.h>
|
||||||
|
|
||||||
static const struct vm_operations_struct xfs_file_vm_ops;
|
static const struct vm_operations_struct xfs_file_vm_ops;
|
||||||
|
|
||||||
@@ -882,6 +883,60 @@ out_unlock:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATIC long
|
||||||
|
xfs_file_fallocate(
|
||||||
|
struct file *file,
|
||||||
|
int mode,
|
||||||
|
loff_t offset,
|
||||||
|
loff_t len)
|
||||||
|
{
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
long error;
|
||||||
|
loff_t new_size = 0;
|
||||||
|
xfs_flock64_t bf;
|
||||||
|
xfs_inode_t *ip = XFS_I(inode);
|
||||||
|
int cmd = XFS_IOC_RESVSP;
|
||||||
|
|
||||||
|
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
bf.l_whence = 0;
|
||||||
|
bf.l_start = offset;
|
||||||
|
bf.l_len = len;
|
||||||
|
|
||||||
|
xfs_ilock(ip, XFS_IOLOCK_EXCL);
|
||||||
|
|
||||||
|
if (mode & FALLOC_FL_PUNCH_HOLE)
|
||||||
|
cmd = XFS_IOC_UNRESVSP;
|
||||||
|
|
||||||
|
/* check the new inode size is valid before allocating */
|
||||||
|
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
||||||
|
offset + len > i_size_read(inode)) {
|
||||||
|
new_size = offset + len;
|
||||||
|
error = inode_newsize_ok(inode, new_size);
|
||||||
|
if (error)
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = -xfs_change_file_space(ip, cmd, &bf, 0, XFS_ATTR_NOLOCK);
|
||||||
|
if (error)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
/* Change file size if needed */
|
||||||
|
if (new_size) {
|
||||||
|
struct iattr iattr;
|
||||||
|
|
||||||
|
iattr.ia_valid = ATTR_SIZE;
|
||||||
|
iattr.ia_size = new_size;
|
||||||
|
error = -xfs_setattr(ip, &iattr, XFS_ATTR_NOLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
STATIC int
|
STATIC int
|
||||||
xfs_file_open(
|
xfs_file_open(
|
||||||
struct inode *inode,
|
struct inode *inode,
|
||||||
@@ -1000,6 +1055,7 @@ const struct file_operations xfs_file_operations = {
|
|||||||
.open = xfs_file_open,
|
.open = xfs_file_open,
|
||||||
.release = xfs_file_release,
|
.release = xfs_file_release,
|
||||||
.fsync = xfs_file_fsync,
|
.fsync = xfs_file_fsync,
|
||||||
|
.fallocate = xfs_file_fallocate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct file_operations xfs_dir_file_operations = {
|
const struct file_operations xfs_dir_file_operations = {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
#include <linux/posix_acl.h>
|
#include <linux/posix_acl.h>
|
||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/falloc.h>
|
|
||||||
#include <linux/fiemap.h>
|
#include <linux/fiemap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
@@ -505,61 +504,6 @@ xfs_vn_setattr(
|
|||||||
return -xfs_setattr(XFS_I(dentry->d_inode), iattr, 0);
|
return -xfs_setattr(XFS_I(dentry->d_inode), iattr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC long
|
|
||||||
xfs_vn_fallocate(
|
|
||||||
struct inode *inode,
|
|
||||||
int mode,
|
|
||||||
loff_t offset,
|
|
||||||
loff_t len)
|
|
||||||
{
|
|
||||||
long error;
|
|
||||||
loff_t new_size = 0;
|
|
||||||
xfs_flock64_t bf;
|
|
||||||
xfs_inode_t *ip = XFS_I(inode);
|
|
||||||
int cmd = XFS_IOC_RESVSP;
|
|
||||||
|
|
||||||
/* preallocation on directories not yet supported */
|
|
||||||
error = -ENODEV;
|
|
||||||
if (S_ISDIR(inode->i_mode))
|
|
||||||
goto out_error;
|
|
||||||
|
|
||||||
bf.l_whence = 0;
|
|
||||||
bf.l_start = offset;
|
|
||||||
bf.l_len = len;
|
|
||||||
|
|
||||||
xfs_ilock(ip, XFS_IOLOCK_EXCL);
|
|
||||||
|
|
||||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
|
||||||
cmd = XFS_IOC_UNRESVSP;
|
|
||||||
|
|
||||||
/* check the new inode size is valid before allocating */
|
|
||||||
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
|
||||||
offset + len > i_size_read(inode)) {
|
|
||||||
new_size = offset + len;
|
|
||||||
error = inode_newsize_ok(inode, new_size);
|
|
||||||
if (error)
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = -xfs_change_file_space(ip, cmd, &bf, 0, XFS_ATTR_NOLOCK);
|
|
||||||
if (error)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
/* Change file size if needed */
|
|
||||||
if (new_size) {
|
|
||||||
struct iattr iattr;
|
|
||||||
|
|
||||||
iattr.ia_valid = ATTR_SIZE;
|
|
||||||
iattr.ia_size = new_size;
|
|
||||||
error = -xfs_setattr(ip, &iattr, XFS_ATTR_NOLOCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
out_unlock:
|
|
||||||
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
|
||||||
out_error:
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define XFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
|
#define XFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -653,7 +597,6 @@ static const struct inode_operations xfs_inode_operations = {
|
|||||||
.getxattr = generic_getxattr,
|
.getxattr = generic_getxattr,
|
||||||
.removexattr = generic_removexattr,
|
.removexattr = generic_removexattr,
|
||||||
.listxattr = xfs_vn_listxattr,
|
.listxattr = xfs_vn_listxattr,
|
||||||
.fallocate = xfs_vn_fallocate,
|
|
||||||
.fiemap = xfs_vn_fiemap,
|
.fiemap = xfs_vn_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ extern struct file *alloc_file(struct path *, fmode_t mode,
|
|||||||
|
|
||||||
static inline void fput_light(struct file *file, int fput_needed)
|
static inline void fput_light(struct file *file, int fput_needed)
|
||||||
{
|
{
|
||||||
if (unlikely(fput_needed))
|
if (fput_needed)
|
||||||
fput(file);
|
fput(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1483,8 +1483,8 @@ struct fiemap_extent_info {
|
|||||||
unsigned int fi_flags; /* Flags as passed from user */
|
unsigned int fi_flags; /* Flags as passed from user */
|
||||||
unsigned int fi_extents_mapped; /* Number of mapped extents */
|
unsigned int fi_extents_mapped; /* Number of mapped extents */
|
||||||
unsigned int fi_extents_max; /* Size of fiemap_extent array */
|
unsigned int fi_extents_max; /* Size of fiemap_extent array */
|
||||||
struct fiemap_extent *fi_extents_start; /* Start of fiemap_extent
|
struct fiemap_extent __user *fi_extents_start; /* Start of
|
||||||
* array */
|
fiemap_extent array */
|
||||||
};
|
};
|
||||||
int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
|
int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
|
||||||
u64 phys, u64 len, u32 flags);
|
u64 phys, u64 len, u32 flags);
|
||||||
@@ -1552,6 +1552,8 @@ struct file_operations {
|
|||||||
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
|
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
|
||||||
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
|
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
|
||||||
int (*setlease)(struct file *, long, struct file_lock **);
|
int (*setlease)(struct file *, long, struct file_lock **);
|
||||||
|
long (*fallocate)(struct file *file, int mode, loff_t offset,
|
||||||
|
loff_t len);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IPERM_FLAG_RCU 0x0001
|
#define IPERM_FLAG_RCU 0x0001
|
||||||
@@ -1582,8 +1584,6 @@ struct inode_operations {
|
|||||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||||
int (*removexattr) (struct dentry *, const char *);
|
int (*removexattr) (struct dentry *, const char *);
|
||||||
void (*truncate_range)(struct inode *, loff_t, loff_t);
|
void (*truncate_range)(struct inode *, loff_t, loff_t);
|
||||||
long (*fallocate)(struct inode *inode, int mode, loff_t offset,
|
|
||||||
loff_t len);
|
|
||||||
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
|
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
|
||||||
u64 len);
|
u64 len);
|
||||||
} ____cacheline_aligned;
|
} ____cacheline_aligned;
|
||||||
|
|||||||
Reference in New Issue
Block a user