You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
don't put symlink bodies in pagecache into highmem
kmap() in page_follow_link_light() needed to go - allowing to hold an arbitrary number of kmaps for long is a great way to deadlocking the system. new helper (inode_nohighmem(inode)) needs to be used for pagecache symlinks inodes; done for all in-tree cases. page_follow_link_light() instrumented to yell about anything missed. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
@@ -504,3 +504,8 @@ in your dentry operations instead.
|
||||
[mandatory]
|
||||
__fd_install() & fd_install() can now sleep. Callers should not
|
||||
hold a spinlock or other resources that do not allow a schedule.
|
||||
--
|
||||
[mandatory]
|
||||
any symlink that might use page_follow_link_light/page_put_link() must
|
||||
have inode_nohighmem(inode) called before anything might start playing with
|
||||
its pagecache.
|
||||
|
||||
@@ -140,6 +140,7 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino)
|
||||
break;
|
||||
case ST_SOFTLINK:
|
||||
inode->i_mode |= S_IFLNK;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_op = &affs_symlink_inode_operations;
|
||||
inode->i_data.a_ops = &affs_symlink_aops;
|
||||
break;
|
||||
|
||||
@@ -344,6 +344,7 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
|
||||
return -ENOSPC;
|
||||
|
||||
inode->i_op = &affs_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_data.a_ops = &affs_symlink_aops;
|
||||
inode->i_mode = S_IFLNK | 0777;
|
||||
mode_to_prot(inode);
|
||||
|
||||
+1
-3
@@ -14,7 +14,7 @@ static int affs_symlink_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct inode *inode = page->mapping->host;
|
||||
char *link = kmap(page);
|
||||
char *link = page_address(page);
|
||||
struct slink_front *lf;
|
||||
int i, j;
|
||||
char c;
|
||||
@@ -57,12 +57,10 @@ static int affs_symlink_readpage(struct file *file, struct page *page)
|
||||
link[i] = '\0';
|
||||
affs_brelse(bh);
|
||||
SetPageUptodate(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
fail:
|
||||
SetPageError(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
|
||||
case AFS_FTYPE_SYMLINK:
|
||||
inode->i_mode = S_IFLNK | vnode->status.mode;
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
break;
|
||||
default:
|
||||
printk("kAFS: AFS vnode with undefined type\n");
|
||||
|
||||
+2
-3
@@ -397,6 +397,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &befs_symlink_aops;
|
||||
} else {
|
||||
inode->i_link = befs_ino->i_data.symlink;
|
||||
@@ -469,7 +470,7 @@ static int befs_symlink_readpage(struct file *unused, struct page *page)
|
||||
struct befs_inode_info *befs_ino = BEFS_I(inode);
|
||||
befs_data_stream *data = &befs_ino->i_data.ds;
|
||||
befs_off_t len = data->size;
|
||||
char *link = kmap(page);
|
||||
char *link = page_address(page);
|
||||
|
||||
if (len == 0 || len > PAGE_SIZE) {
|
||||
befs_error(sb, "Long symlink with illegal length");
|
||||
@@ -483,12 +484,10 @@ static int befs_symlink_readpage(struct file *unused, struct page *page)
|
||||
}
|
||||
link[len - 1] = '\0';
|
||||
SetPageUptodate(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
fail:
|
||||
SetPageError(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -3774,6 +3774,7 @@ cache_acl:
|
||||
break;
|
||||
case S_IFLNK:
|
||||
inode->i_op = &btrfs_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &btrfs_symlink_aops;
|
||||
break;
|
||||
default:
|
||||
@@ -9705,6 +9706,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
btrfs_free_path(path);
|
||||
|
||||
inode->i_op = &btrfs_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &btrfs_symlink_aops;
|
||||
inode_set_bytes(inode, name_len);
|
||||
btrfs_i_size_write(inode, name_len);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/coda.h>
|
||||
#include <linux/coda_psdev.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "coda_linux.h"
|
||||
|
||||
static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
|
||||
@@ -35,6 +36,7 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
|
||||
inode->i_fop = &coda_dir_operations;
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
inode->i_op = &coda_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_data.a_ops = &coda_symlink_aops;
|
||||
inode->i_mapping = &inode->i_data;
|
||||
} else
|
||||
|
||||
+1
-3
@@ -26,7 +26,7 @@ static int coda_symlink_filler(struct file *file, struct page *page)
|
||||
int error;
|
||||
struct coda_inode_info *cii;
|
||||
unsigned int len = PAGE_SIZE;
|
||||
char *p = kmap(page);
|
||||
char *p = page_address(page);
|
||||
|
||||
cii = ITOC(inode);
|
||||
|
||||
@@ -34,13 +34,11 @@ static int coda_symlink_filler(struct file *file, struct page *page)
|
||||
if (error)
|
||||
goto fail;
|
||||
SetPageUptodate(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
SetPageError(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb,
|
||||
break;
|
||||
case S_IFLNK:
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_data.a_ops = &cramfs_aops;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -151,6 +151,7 @@ struct inode *efs_iget(struct super_block *super, unsigned long ino)
|
||||
break;
|
||||
case S_IFLNK:
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_data.a_ops = &efs_symlink_aops;
|
||||
break;
|
||||
case S_IFCHR:
|
||||
|
||||
+1
-3
@@ -13,7 +13,7 @@
|
||||
|
||||
static int efs_symlink_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
char *link = kmap(page);
|
||||
char *link = page_address(page);
|
||||
struct buffer_head * bh;
|
||||
struct inode * inode = page->mapping->host;
|
||||
efs_block_t size = inode->i_size;
|
||||
@@ -39,12 +39,10 @@ static int efs_symlink_readpage(struct file *file, struct page *page)
|
||||
}
|
||||
link[size] = '\0';
|
||||
SetPageUptodate(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
fail:
|
||||
SetPageError(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1227,6 +1227,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
|
||||
inode->i_link = (char *)oi->i_data;
|
||||
} else {
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &exofs_aops;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -111,6 +111,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
if (l > sizeof(oi->i_data)) {
|
||||
/* slow symlink */
|
||||
inode->i_op = &page_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &exofs_aops;
|
||||
memset(oi->i_data, 0, sizeof(oi->i_data));
|
||||
|
||||
|
||||
@@ -1420,6 +1420,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
|
||||
sizeof(ei->i_data) - 1);
|
||||
} else {
|
||||
inode->i_op = &ext2_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
if (test_opt(inode->i_sb, NOBH))
|
||||
inode->i_mapping->a_ops = &ext2_nobh_aops;
|
||||
else
|
||||
|
||||
@@ -183,6 +183,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
|
||||
if (l > sizeof (EXT2_I(inode)->i_data)) {
|
||||
/* slow symlink */
|
||||
inode->i_op = &ext2_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
if (test_opt(inode->i_sb, NOBH))
|
||||
inode->i_mapping->a_ops = &ext2_nobh_aops;
|
||||
else
|
||||
|
||||
@@ -4283,6 +4283,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
||||
inode->i_op = &ext4_symlink_inode_operations;
|
||||
ext4_set_aops(inode);
|
||||
}
|
||||
inode_nohighmem(inode);
|
||||
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
|
||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
inode->i_op = &ext4_special_inode_operations;
|
||||
|
||||
@@ -3132,6 +3132,7 @@ static int ext4_symlink(struct inode *dir,
|
||||
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
|
||||
if (!encryption_required)
|
||||
inode->i_op = &ext4_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
ext4_set_aops(inode);
|
||||
/*
|
||||
* We cannot call page_symlink() with transaction started
|
||||
|
||||
+3
-7
@@ -45,7 +45,7 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
|
||||
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
|
||||
if (IS_ERR(cpage))
|
||||
return ERR_CAST(cpage);
|
||||
caddr = kmap(cpage);
|
||||
caddr = page_address(cpage);
|
||||
caddr[size] = 0;
|
||||
}
|
||||
|
||||
@@ -75,16 +75,12 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
|
||||
/* Null-terminate the name */
|
||||
if (res <= plen)
|
||||
paddr[res] = '\0';
|
||||
if (cpage) {
|
||||
kunmap(cpage);
|
||||
if (cpage)
|
||||
page_cache_release(cpage);
|
||||
}
|
||||
return *cookie = paddr;
|
||||
errout:
|
||||
if (cpage) {
|
||||
kunmap(cpage);
|
||||
if (cpage)
|
||||
page_cache_release(cpage);
|
||||
}
|
||||
kfree(paddr);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
@@ -202,6 +202,7 @@ make_now:
|
||||
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
|
||||
else
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
|
||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user