diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-11 13:13:23 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-11 13:13:23 -0800 |
commit | 32fb378437a1d716e72a442237d7ead1f435ecf0 (patch) | |
tree | 411b25023d4df908fb8ca4517185d49c37c51e10 /fs | |
parent | 19ccb28e296d5afa299db1003d37e5d37994d46e (diff) | |
parent | fceef393a538134f03b778c5d2519e670269342f (diff) |
Merge branch 'work.symlinks' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs RCU symlink updates from Al Viro:
"Replacement of ->follow_link/->put_link, allowing to stay in RCU mode
even if the symlink is not an embedded one.
No changes since the mailbomb on Jan 1"
* 'work.symlinks' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
switch ->get_link() to delayed_call, kill ->put_link()
kill free_page_put_link()
teach nfs_get_link() to work in RCU mode
teach proc_self_get_link()/proc_thread_self_get_link() to work in RCU mode
teach shmem_get_link() to work in RCU mode
teach page_get_link() to work in RCU mode
replace ->follow_link() with new method that could stay in RCU mode
don't put symlink bodies in pagecache into highmem
namei: page_getlink() and page_follow_link_light() are the same thing
ufs: get rid of ->setattr() for symlinks
udf: don't duplicate page_symlink_inode_operations
logfs: don't duplicate page_symlink_inode_operations
switch befs long symlinks to page_symlink_operations
Diffstat (limited to 'fs')
87 files changed, 456 insertions, 417 deletions
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 511078586fa1..c7cc7c30f0c8 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1223,18 +1223,26 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) } /** - * v9fs_vfs_follow_link - follow a symlink path + * v9fs_vfs_get_link - follow a symlink path * @dentry: dentry for symlink - * @cookie: place to pass the data to put_link() + * @inode: inode for symlink + * @done: delayed call for when we are done with the return value */ -static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie) +static const char *v9fs_vfs_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); - struct p9_fid *fid = v9fs_fid_lookup(dentry); + struct v9fs_session_info *v9ses; + struct p9_fid *fid; struct p9_wstat *st; char *res; + if (!dentry) + return ERR_PTR(-ECHILD); + + v9ses = v9fs_dentry2v9ses(dentry); + fid = v9fs_fid_lookup(dentry); p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); if (IS_ERR(fid)) @@ -1259,7 +1267,8 @@ static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie) p9stat_free(st); kfree(st); - return *cookie = res; + set_delayed_call(done, kfree_link, res); + return res; } /** @@ -1452,8 +1461,7 @@ static const struct inode_operations v9fs_file_inode_operations = { static const struct inode_operations v9fs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = v9fs_vfs_follow_link, - .put_link = kfree_put_link, + .get_link = v9fs_vfs_get_link, .getattr = v9fs_vfs_getattr, .setattr = v9fs_vfs_setattr, }; diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index cb899af1babc..a34702c998f5 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -899,26 +899,34 @@ error: } /** - * v9fs_vfs_follow_link_dotl - follow a symlink path + * v9fs_vfs_get_link_dotl - follow a symlink path * @dentry: dentry for symlink - * @cookie: place to pass the data to put_link() + * @inode: inode for symlink + * @done: destructor for return value */ static const char * -v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie) +v9fs_vfs_get_link_dotl(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct p9_fid *fid = v9fs_fid_lookup(dentry); + struct p9_fid *fid; char *target; int retval; + if (!dentry) + return ERR_PTR(-ECHILD); + p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); + fid = v9fs_fid_lookup(dentry); if (IS_ERR(fid)) return ERR_CAST(fid); retval = p9_client_readlink(fid, &target); if (retval) return ERR_PTR(retval); - return *cookie = target; + set_delayed_call(done, kfree_link, target); + return target; } int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) @@ -984,8 +992,7 @@ const struct inode_operations v9fs_file_inode_operations_dotl = { const struct inode_operations v9fs_symlink_inode_operations_dotl = { .readlink = generic_readlink, - .follow_link = v9fs_vfs_follow_link_dotl, - .put_link = kfree_put_link, + .get_link = v9fs_vfs_get_link_dotl, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, .setxattr = generic_setxattr, diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 17349500592d..0fdb0f5b2239 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -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; diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 181e05b46e72..00d3002a6780 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -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); diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index ea5b69a18ba9..69b03dbb792f 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -14,13 +14,13 @@ 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; char lc; - pr_debug("follow_link(ino=%lu)\n", inode->i_ino); + pr_debug("get_link(ino=%lu)\n", inode->i_ino); bh = affs_bread(inode->i_sb, inode->i_ino); if (!bh) @@ -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; } @@ -73,7 +71,6 @@ const struct address_space_operations affs_symlink_aops = { const struct inode_operations affs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .setattr = affs_notify_change, }; diff --git a/fs/afs/inode.c b/fs/afs/inode.c index e06f5a23352a..86cc7264c21c 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -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"); diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c index da0c33481bc0..84e037d1d129 100644 --- a/fs/autofs4/symlink.c +++ b/fs/autofs4/symlink.c @@ -12,10 +12,16 @@ #include "autofs_i.h" -static const char *autofs4_follow_link(struct dentry *dentry, void **cookie) +static const char *autofs4_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_sb_info *sbi; + struct autofs_info *ino; + if (!dentry) + return ERR_PTR(-ECHILD); + sbi = autofs4_sbi(dentry->d_sb); + ino = autofs4_dentry_ino(dentry); if (ino && !autofs4_oz_mode(sbi)) ino->last_used = jiffies; return d_inode(dentry)->i_private; @@ -23,5 +29,5 @@ static const char *autofs4_follow_link(struct dentry *dentry, void **cookie) const struct inode_operations autofs4_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = autofs4_follow_link + .get_link = autofs4_get_link }; diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 46aedacfa6a8..25250fa87086 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -42,7 +42,7 @@ static struct inode *befs_iget(struct super_block *, unsigned long); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static void befs_destroy_inodecache(void); -static const char *befs_follow_link(struct dentry *, void **); +static int befs_symlink_readpage(struct file *, struct page *); static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, @@ -79,10 +79,8 @@ static const struct address_space_operations befs_aops = { .bmap = befs_bmap, }; -static const struct inode_operations befs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = befs_follow_link, - .put_link = kfree_put_link, +static const struct address_space_operations befs_symlink_aops = { + .readpage = befs_symlink_readpage, }; /* @@ -398,7 +396,9 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) inode->i_fop = &befs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { - inode->i_op = &befs_symlink_inode_operations; + 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; inode->i_op = &simple_symlink_inode_operations; @@ -463,31 +463,33 @@ befs_destroy_inodecache(void) * The data stream become link name. Unless the LONG_SYMLINK * flag is set. */ -static const char * -befs_follow_link(struct dentry *dentry, void **cookie) +static int befs_symlink_readpage(struct file *unused, struct page *page) { - struct super_block *sb = dentry->d_sb; - struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry)); + struct inode *inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + 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; + char *link = page_address(page); - if (len == 0) { + if (len == 0 || len > PAGE_SIZE) { befs_error(sb, "Long symlink with illegal length"); - return ERR_PTR(-EIO); + goto fail; } befs_debug(sb, "Follow long symlink"); - link = kmalloc(len, GFP_NOFS); - if (!link) - return ERR_PTR(-ENOMEM); if (befs_read_lsymlink(sb, data, link, len) != len) { - kfree(link); befs_error(sb, "Failed to read entire long symlink"); - return ERR_PTR(-EIO); + goto fail; } link[len - 1] = '\0'; - return *cookie = link; + SetPageUptodate(page); + unlock_page(page); + return 0; +fail: + SetPageError(page); + unlock_page(page); + return -EIO; } /* diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a70c5790f8f5..1a41a65fd2ff 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -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); @@ -10094,8 +10096,7 @@ static const struct inode_operations btrfs_special_inode_operations = { }; static const struct inode_operations btrfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .getattr = btrfs_getattr, .setattr = btrfs_setattr, .permission = btrfs_permission, diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 498dcfa2dcdb..da55eb8bcffa 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1756,7 +1756,7 @@ retry: */ static const struct inode_operations ceph_symlink_iops = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ceph_setattr, .getattr = ceph_getattr, .setxattr = ceph_setxattr, diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index cbc0f4bca0c0..90e4e2b398b6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -900,8 +900,7 @@ const struct inode_operations cifs_file_inode_ops = { const struct inode_operations cifs_symlink_inode_ops = { .readlink = generic_readlink, - .follow_link = cifs_follow_link, - .put_link = kfree_put_link, + .get_link = cifs_get_link, .permission = cifs_permission, /* BB add the following two eventually */ /* revalidate: cifs_revalidate, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index c3cc1609025f..26a1187d4323 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -120,9 +120,8 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path); #endif /* Functions related to symlinks */ -extern const char *cifs_follow_link(struct dentry *direntry, void **cookie); -extern int cifs_readlink(struct dentry *direntry, char __user *buffer, - int buflen); +extern const char *cifs_get_link(struct dentry *, struct inode *, + struct delayed_call *); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname); extern int cifs_removexattr(struct dentry *, const char *); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index e3548f73bdea..062c2375549a 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -627,9 +627,9 @@ cifs_hl_exit: } const char * -cifs_follow_link(struct dentry *direntry, void **cookie) +cifs_get_link(struct dentry *direntry, struct inode *inode, + struct delayed_call *done) { - struct inode *inode = d_inode(direntry); int rc = -ENOMEM; unsigned int xid; char *full_path = NULL; @@ -639,6 +639,9 @@ cifs_follow_link(struct dentry *direntry, void **cookie) struct cifs_tcon *tcon; struct TCP_Server_Info *server; + if (!direntry) + return ERR_PTR(-ECHILD); + xid = get_xid(); tlink = cifs_sb_tlink(cifs_sb); @@ -678,7 +681,8 @@ cifs_follow_link(struct dentry *direntry, void **cookie) kfree(target_path); return ERR_PTR(rc); } - return *cookie = target_path; + set_delayed_call(done, kfree_link, target_path); + return target_path; } int diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 7740b1c871c1..1bfb7ba4e85e 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -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) @@ -17,8 +18,7 @@ static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2) static const struct inode_operations coda_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .setattr = coda_setattr, }; @@ -35,6 +35,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 diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index ab94ef63caef..03736e20d720 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -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; } diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index ec5c8325b503..db6d69289608 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -279,27 +279,33 @@ static int configfs_getlink(struct dentry *dentry, char * path) } -static const char *configfs_follow_link(struct dentry *dentry, void **cookie) +static const char *configfs_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - unsigned long page = get_zeroed_page(GFP_KERNEL); + char *body; int error; - if (!page) + if (!dentry) + return ERR_PTR(-ECHILD); + + body = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!body) return ERR_PTR(-ENOMEM); - error = configfs_getlink(dentry, (char *)page); + error = configfs_getlink(dentry, body); if (!error) { - return *cookie = (void *)page; + set_delayed_call(done, kfree_link, body); + return body; } - free_page(page); + kfree(body); return ERR_PTR(error); } const struct inode_operations configfs_symlink_inode_operations = { - .follow_link = configfs_follow_link, + .get_link = configfs_get_link, .readlink = generic_readlink, - .put_link = free_page_put_link, .setattr = configfs_setattr, }; diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 355c522f3585..b862bc219cd7 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -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: diff --git a/fs/dcache.c b/fs/dcache.c index 5c33aeb0f68f..d27f0909d9f6 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1734,7 +1734,7 @@ static unsigned d_flags_for_inode(struct inode *inode) } if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) { - if (unlikely(inode->i_op->follow_link)) { + if (unlikely(inode->i_op->get_link)) { add_flags = DCACHE_SYMLINK_TYPE; goto type_determined; } diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index e2e47ba5d313..a4dddc61594c 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -674,16 +674,24 @@ out: return rc ? ERR_PTR(rc) : buf; } -static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie) +static const char *ecryptfs_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { size_t len; - char *buf = ecryptfs_readlink_lower(dentry, &len); + char *buf; + + if (!dentry) + return ERR_PTR(-ECHILD); + + buf = ecryptfs_readlink_lower(dentry, &len); if (IS_ERR(buf)) return buf; fsstack_copy_attr_atime(d_inode(dentry), d_inode(ecryptfs_dentry_to_lower(dentry))); buf[len] = '\0'; - return *cookie = buf; + set_delayed_call(done, kfree_link, buf); + return buf; } /** @@ -1095,8 +1103,7 @@ out: const struct inode_operations ecryptfs_symlink_iops = { .readlink = generic_readlink, - .follow_link = ecryptfs_follow_link, - .put_link = kfree_put_link, + .get_link = ecryptfs_get_link, .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, .getattr = ecryptfs_getattr_link, diff --git a/fs/efs/inode.c b/fs/efs/inode.c index 079d20306ee1..cdf0872382af 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -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: diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c index 75117d0dac2b..4870cc82deb0 100644 --- a/fs/efs/symlink.c +++ b/fs/efs/symlink.c @@ -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; } diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 60f03b78914e..9eaf595aeaf8 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1224,6 +1224,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 { diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c index 994e078da4bb..c20d77df2679 100644 --- a/fs/exofs/namei.c +++ b/fs/exofs/namei.c @@ -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)); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 0aa9bf6e6e53..338eefda70c6 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -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 diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 3267a80dbbe2..7a2be8f7f3c3 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -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 diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index ae17179f3810..3495d8ae4b33 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -22,8 +22,7 @@ const struct inode_operations ext2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, @@ -35,7 +34,7 @@ const struct inode_operations ext2_symlink_inode_operations = { const struct inode_operations ext2_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ea433a7f4bca..b3bd912df6bf 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -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; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index a969ab39f302..f27e0c2598c5 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -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 diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index e8e7af62ac95..6f7ee30a89ce 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -23,17 +23,21 @@ #include "xattr.h" #ifdef CONFIG_EXT4_FS_ENCRYPTION -static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cookie) +static const char *ext4_encrypted_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { struct page *cpage = NULL; char *caddr, *paddr = NULL; struct ext4_str cstr, pstr; - struct inode *inode = d_inode(dentry); struct ext4_encrypted_symlink_data *sd; loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); int res; u32 plen, max_size = inode->i_sb->s_blocksize; + if (!dentry) + return ERR_PTR(-ECHILD); + res = ext4_get_encryption_info(inode); if (res) return ERR_PTR(res); @@ -45,7 +49,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,24 +79,20 @@ 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; + set_delayed_call(done, kfree_link, paddr); + return paddr; errout: - if (cpage) { - kunmap(cpage); + if (cpage) page_cache_release(cpage); - } kfree(paddr); return ERR_PTR(res); } const struct inode_operations ext4_encrypted_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext4_encrypted_follow_link, - .put_link = kfree_put_link, + .get_link = ext4_encrypted_get_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, @@ -103,8 +103,7 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = { const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, @@ -114,7 +113,7 @@ const struct inode_operations ext4_symlink_inode_operations = { const struct inode_operations ext4_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 97e20decacb4..5528801a5baf 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -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)) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 2c32110f9fc0..e7587fce1b80 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -315,12 +315,15 @@ fail: return err; } -static const char *f2fs_follow_link(struct dentry *dentry, void **cookie) +static const char *f2fs_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - const char *link = page_follow_link_light(dentry, cookie); + const char *link = page_get_link(dentry, inode, done); if (!IS_ERR(link) && !*link) { /* this is broken symlink case */ - page_put_link(NULL, *cookie); + do_delayed_call(done); + clear_delayed_call(done); link = ERR_PTR(-ENOENT); } return link; @@ -351,6 +354,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, 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; f2fs_lock_op(sbi); @@ -923,18 +927,22 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, } #ifdef CONFIG_F2FS_FS_ENCRYPTION -static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cookie) +static const char *f2fs_encrypted_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { struct page *cpage = NULL; char *caddr, *paddr = NULL; struct f2fs_str cstr; struct f2fs_str pstr = FSTR_INIT(NULL, 0); - struct inode *inode = d_inode(dentry); struct f2fs_encrypted_symlink_data *sd; loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); u32 max_size = inode->i_sb->s_blocksize; int res; + if (!dentry) + return ERR_PTR(-ECHILD); + res = f2fs_get_encryption_info(inode); if (res) return ERR_PTR(res); @@ -942,7 +950,7 @@ static const char *f2fs_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; /* Symlink is encrypted */ @@ -982,21 +990,19 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook /* Null-terminate the name */ paddr[res] = '\0'; - kunmap(cpage); page_cache_release(cpage); - return *cookie = paddr; + set_delayed_call(done, kfree_link, paddr); + return paddr; errout: kfree(cstr.name); f2fs_fname_crypto_free_buffer(&pstr); - kunmap(cpage); page_cache_release(cpage); return ERR_PTR(res); } const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = f2fs_encrypted_follow_link, - .put_link = kfree_put_link, + .get_link = f2fs_encrypted_get_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, .setxattr = generic_setxattr, @@ -1031,8 +1037,7 @@ const struct inode_operations f2fs_dir_inode_operations = { const struct inode_operations f2fs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = f2fs_follow_link, - .put_link = page_put_link, + .get_link = f2fs_get_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, #ifdef CONFIG_F2FS_FS_XATTR diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index ef73ed674a27..3e2ccade61ed 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -326,6 +326,7 @@ vxfs_iget(struct super_block *sbp, ino_t ino) } else if (S_ISLNK(ip->i_mode)) { if (!VXFS_ISIMMED(vip)) { ip->i_op = &page_symlink_inode_operations; + inode_nohighmem(ip); ip->i_mapping->a_ops = &vxfs_aops; } else { ip->i_op = &simple_symlink_inode_operations; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 5e2e08712d3b..712601f299b8 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1365,15 +1365,19 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) return err; } -static const char *fuse_follow_link(struct dentry *dentry, void **cookie) +static const char *fuse_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct inode *inode = d_inode(dentry); struct fuse_conn *fc = get_fuse_conn(inode); FUSE_ARGS(args); char *link; ssize_t ret; - link = (char *) __get_free_page(GFP_KERNEL); + if (!dentry) + return ERR_PTR(-ECHILD); + + link = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!link) return ERR_PTR(-ENOMEM); @@ -1385,11 +1389,11 @@ static const char *fuse_follow_link(struct dentry *dentry, void **cookie) args.out.args[0].value = link; ret = fuse_simple_request(fc, &args); if (ret < 0) { - free_page((unsigned long) link); + kfree(link); link = ERR_PTR(ret); } else { link[ret] = '\0'; - *cookie = link; + set_delayed_call(done, kfree_link, link); } fuse_invalidate_atime(inode); return link; @@ -1909,8 +1913,7 @@ static const struct inode_operations fuse_common_inode_operations = { static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, - .follow_link = fuse_follow_link, - .put_link = free_page_put_link, + .get_link = fuse_get_link, .readlink = generic_readlink, .getattr = fuse_getattr, .setxattr = fuse_setxattr, diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 063fdfcf8275..1bae189f3245 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1712,24 +1712,30 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry, } /** - * gfs2_follow_link - Follow a symbolic link + * gfs2_get_link - Follow a symbolic link * @dentry: The dentry of the link - * @nd: Data that we pass to vfs_follow_link() + * @inode: The inode of the link + * @done: destructor for return value * * This can handle symlinks of any size. * * Returns: 0 on success or error code */ -static const char *gfs2_follow_link(struct dentry *dentry, void **cookie) +static const char *gfs2_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct gfs2_inode *ip = GFS2_I(d_inode(dentry)); + struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder i_gh; struct buffer_head *dibh; unsigned int size; char *buf; int error; + if (!dentry) + return ERR_PTR(-ECHILD); + gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh); error = gfs2_glock_nq(&i_gh); if (error) { @@ -1759,7 +1765,7 @@ static const char *gfs2_follow_link(struct dentry *dentry, void **cookie) out: gfs2_glock_dq_uninit(&i_gh); if (!IS_ERR(buf)) - *cookie = buf; + set_delayed_call(done, kfree_link, buf); return buf; } @@ -2132,8 +2138,7 @@ const struct inode_operations gfs2_dir_iops = { const struct inode_operations gfs2_symlink_iops = { .readlink = generic_readlink, - .follow_link = gfs2_follow_link, - .put_link = kfree_put_link, + .get_link = gfs2_get_link, .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 6dd107d7421e..19b33f8151f1 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -403,6 +403,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode) } else if (S_ISLNK(inode->i_mode)) { sbi->file_count++; inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &hfsplus_aops; hip->clump_blocks = 1; } else @@ -526,6 +527,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) inode->i_mapping->a_ops = &hfsplus_aops; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &hfsplus_aops; } else { init_special_inode(inode, inode->i_mode, diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 2ac99db3750e..7db524cc85b6 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -892,9 +892,14 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -static const char *hostfs_follow_link(struct dentry *dentry, void **cookie) +static const char *hostfs_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - char *link = __getname(); + char *link; + if (!dentry) + return ERR_PTR(-ECHILD); + link = kmalloc(PATH_MAX, GFP_KERNEL); if (link) { char *path = dentry_name(dentry); int err = -ENOMEM; @@ -905,25 +910,20 @@ static const char *hostfs_follow_link(struct dentry *dentry, void **cookie) __putname(path); } if (err < 0) { - __putname(link); + kfree(link); return ERR_PTR(err); } } else { return ERR_PTR(-ENOMEM); } - return *cookie = link; -} - -static void hostfs_put_link(struct inode *unused, void *cookie) -{ - __putname(cookie); + set_delayed_call(done, kfree_link, link); + return link; } static const struct inode_operations hostfs_link_iops = { .readlink = generic_readlink, - .follow_link = hostfs_follow_link, - .put_link = hostfs_put_link, + .get_link = hostfs_get_link, }; static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 933c73780813..1f3c6d76200b 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -77,6 +77,7 @@ void hpfs_read_inode(struct inode *i) kfree(ea); i->i_mode = S_IFLNK | 0777; i->i_op = &page_symlink_inode_operations; + inode_nohighmem(i); i->i_data.a_ops = &hpfs_symlink_aops; set_nlink(i, 1); i->i_size = ea_size; diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index ae4d5a1fa4c9..506765afa1a3 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -332,6 +332,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy result->i_blocks = 1; set_nlink(result, 1); result->i_size = strlen(symlink); + inode_nohighmem(result); result->i_op = &page_symlink_inode_operations; result->i_data.a_ops = &hpfs_symlink_aops; @@ -500,7 +501,7 @@ out: static int hpfs_symlink_readpage(struct file *file, struct page *page) { - char *link = kmap(page); + char *link = page_address(page); struct inode *i = page->mapping->host; struct fnode *fnode; struct buffer_head *bh; @@ -516,14 +517,12 @@ static int hpfs_symlink_readpage(struct file *file, struct page *page) goto fail; hpfs_unlock(i->i_sb); SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; fail: hpfs_unlock(i->i_sb); SetPageError(page); - kunmap(page); unlock_page(page); return err; } diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index de4bdfac0cec..d8f51ee8126b 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -760,6 +760,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); break; } lockdep_annotate_inode_mutex_key(inode); diff --git a/fs/inode.c b/fs/inode.c index 1be5f9003eb3..5bb85a064ce7 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2028,3 +2028,9 @@ void inode_set_flags(struct inode *inode, unsigned int flags, new_flags) != old_flags)); } EXPORT_SYMBOL(inode_set_flags); + +void inode_nohighmem(struct inode *inode) +{ + mapping_set_gfp_mask(inode->i_mapping, GFP_USER); +} +EXPORT_SYMBOL(inode_nohighmem); diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d67a16f2a45d..61abdc4920da 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1417,6 +1417,7 @@ static int isofs_read_inode(struct inode *inode, int relocated) inode->i_fop = &isofs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_data.a_ops = &isofs_symlink_aops; } else /* XXX - parse_rock_ridge_inode() had already set i_rdev. */ diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 735d7522a3a9..5384ceb35b1c 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -687,7 +687,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) struct inode *inode = page->mapping->host; struct iso_inode_info *ei = ISOFS_I(inode); struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); - char *link = kmap(page); + char *link = page_address(page); unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); struct buffer_head *bh; char *rpnt = link; @@ -774,7 +774,6 @@ repeat: brelse(bh); *rpnt = '\0'; SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; @@ -791,7 +790,6 @@ fail: brelse(bh); error: SetPageError(page); - kunmap(page); unlock_page(page); return -EIO; } diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 8ce2f240125b..2cabd649d4fb 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -14,7 +14,7 @@ const struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = jffs2_setattr, .setxattr = jffs2_setxattr, .getxattr = jffs2_getxattr, diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 41aa3ca6a6a4..9d9bae63ae2a 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -60,6 +60,7 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino) } else if (S_ISLNK(inode->i_mode)) { if (inode->i_size >= IDATASIZE) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &jfs_aops; } else { inode->i_op = &jfs_fast_symlink_inode_operations; diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 9d7551f5c32a..701f89370de7 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -983,6 +983,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, jfs_info("jfs_symlink: allocate extent ip:0x%p", ip); ip->i_op = &jfs_symlink_inode_operations; + inode_nohighmem(ip); ip->i_mapping->a_ops = &jfs_aops; /* diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c index 5929e2363cb8..f8db4fde0b0b 100644 --- a/fs/jfs/symlink.c +++ b/fs/jfs/symlink.c @@ -23,7 +23,7 @@ const struct inode_operations jfs_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr, @@ -33,8 +33,7 @@ const struct inode_operations jfs_fast_symlink_inode_operations = { const struct inode_operations jfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr, diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index db272528ab5b..117b8b3416f9 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -112,18 +112,25 @@ static int kernfs_getlink(struct dentry *dentry, char *path) return error; } -static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie) +static const char *kernfs_iop_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - int error = -ENOMEM; - unsigned long page = get_zeroed_page(GFP_KERNEL); - if (!page) + char *body; + int error; + + if (!dentry) + return ERR_PTR(-ECHILD); + body = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!body) return ERR_PTR(-ENOMEM); - error = kernfs_getlink(dentry, (char *)page); + error = kernfs_getlink(dentry, body); if (unlikely(error < 0)) { - free_page((unsigned long)page); + kfree(body); return ERR_PTR(error); } - return *cookie = (char *)page; + set_delayed_call(done, kfree_link, body); + return body; } const struct inode_operations kernfs_symlink_iops = { @@ -132,8 +139,7 @@ const struct inode_operations kernfs_symlink_iops = { .getxattr = kernfs_iop_getxattr, .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, - .follow_link = kernfs_iop_follow_link, - .put_link = free_page_put_link, + .get_link = kernfs_iop_get_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, .permission = kernfs_iop_permission, diff --git a/fs/libfs.c b/fs/libfs.c index c7cbfb092e94..01491299f348 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1019,17 +1019,12 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync) } EXPORT_SYMBOL(noop_fsync); -void kfree_put_link(struct inode *unused, void *cookie) +/* Because kfree isn't assignment-compatible with void(void*) ;-/ */ +void kfree_link(void *p) { - kfree(cookie); + kfree(p); } -EXPORT_SYMBOL(kfree_put_link); - -void free_page_put_link(struct inode *unused, void *cookie) -{ - free_page((unsigned long) cookie); -} -EXPORT_SYMBOL(free_page_put_link); +EXPORT_SYMBOL(kfree_link); /* * nop .set_page_dirty method so that people can use .page_mkwrite on @@ -1092,14 +1087,15 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, } EXPORT_SYMBOL(simple_nosetlease); -const char *simple_follow_link(struct dentry *dentry, void **cookie) +const char *simple_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) { - return d_inode(dentry)->i_link; + return inode->i_link; } -EXPORT_SYMBOL(simple_follow_link); +EXPORT_SYMBOL(simple_get_link); const struct inode_operations simple_symlink_inode_operations = { - .follow_link = simple_follow_link, + .get_link = simple_get_link, .readlink = generic_readlink }; EXPORT_SYMBOL(simple_symlink_inode_operations); diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index f9b45d46d4c4..542468e9bfb4 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -528,7 +528,8 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry, if (IS_ERR(inode)) return PTR_ERR(inode); - inode->i_op = &logfs_symlink_iops; + inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &logfs_reg_aops; return __logfs_create(dir, dentry, inode, target, destlen); @@ -776,12 +777,6 @@ fail: return -EIO; } -const struct inode_operations logfs_symlink_iops = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -}; - const struct inode_operations logfs_dir_iops = { .create = logfs_create, .link = logfs_link, diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c index af49e2d6941a..0fce46d62b9c 100644 --- a/fs/logfs/inode.c +++ b/fs/logfs/inode.c @@ -64,7 +64,8 @@ static void logfs_inode_setops(struct inode *inode) inode->i_mapping->a_ops = &logfs_reg_aops; break; case S_IFLNK: - inode->i_op = &logfs_symlink_iops; + inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &logfs_reg_aops; break; case S_IFSOCK: /* fall through */ diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 5f0937609465..209a26d84c38 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -495,7 +495,6 @@ static inline int logfs_get_sb_mtd(struct logfs_super *s, int mtdnr) #endif /* dir.c */ -extern const struct inode_operations logfs_symlink_iops; extern const struct inode_operations logfs_dir_iops; extern const struct file_operations logfs_dir_fops; int logfs_replay_journal(struct super_block *sb); diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 086cd0a61e80..cb1789ca1ee6 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -435,8 +435,7 @@ static const struct address_space_operations minix_aops = { static const struct inode_operations minix_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .getattr = minix_getattr, }; @@ -452,6 +451,7 @@ void minix_set_inode(struct inode *inode, dev_t rdev) inode->i_mapping->a_ops = &minix_aops; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &minix_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &minix_aops; } else init_special_inode(inode, inode->i_mode, rdev); diff --git a/fs/namei.c b/fs/namei.c index 0c3974cd3ecd..3c909aebef70 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -505,13 +505,13 @@ struct nameidata { int total_link_count; struct saved { struct path link; - void *cookie; + struct delayed_call done; const char *name; - struct inode *inode; unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; struct filename *name; struct nameidata *saved; + struct inode *link_inode; unsigned root_seq; int dfd; }; @@ -592,11 +592,8 @@ static void drop_links(struct nameidata *nd) int i = nd->depth; while (i--) { struct saved *last = nd->stack + i; - struct inode *inode = last->inode; - if (last->cookie && inode->i_op->put_link) { - inode->i_op->put_link(inode, last->cookie); - last->cookie = NULL; - } + do_delayed_call(&last->done); + clear_delayed_call(&last->done); } } @@ -842,7 +839,7 @@ static inline void path_to_nameidata(const struct path *path, } /* - * Helper to directly jump to a known parsed path from ->follow_link, + * Helper to directly jump to a known parsed path from ->get_link, * caller must have taken a reference to path beforehand. */ void nd_jump_link(struct path *path) @@ -858,9 +855,7 @@ void nd_jump_link(struct path *path) static inline void put_link(struct nameidata *nd) { struct saved *last = nd->stack + --nd->depth; - struct inode *inode = last->inode; - if (last->cookie && inode->i_op->put_link) - inode->i_op->put_link(inode, last->cookie); + do_delayed_call(&last->done); if (!(nd->flags & LOOKUP_RCU)) path_put(&last->link); } @@ -892,7 +887,7 @@ static inline int may_follow_link(struct nameidata *nd) return 0; /* Allowed if owner and follower match. */ - inode = nd->stack[0].inode; + inode = nd->link_inode; if (uid_eq(current_cred()->fsuid, inode->i_uid)) return 0; @@ -983,7 +978,7 @@ const char *get_link(struct nameidata *nd) { struct saved *last = nd->stack + nd->depth - 1; struct dentry *dentry = last->link.dentry; - struct inode *inode = last->inode; + struct inode *inode = nd->link_inode; int error; const char *res; @@ -1004,15 +999,21 @@ const char *get_link(struct nameidata *nd) nd->last_type = LAST_BIND; res = inode->i_link; if (!res) { + const char * (*get)(struct dentry *, struct inode *, + struct delayed_call *); + get = inode->i_op->get_link; if (nd->flags & LOOKUP_RCU) { - if (unlikely(unlazy_walk(nd, NULL, 0))) - return ERR_PTR(-ECHILD); + res = get(NULL, inode, &last->done); + if (res == ERR_PTR(-ECHILD)) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); + res = get(dentry, inode, &last->done); + } + } else { + res = get(dentry, inode, &last->done); } - res = inode->i_op->follow_link(dentry, &last->cookie); - if (IS_ERR_OR_NULL(res)) { - last->cookie = NULL; + if (IS_ERR_OR_NULL(res)) return res; - } } if (*res == '/') { if (nd->flags & LOOKUP_RCU) { @@ -1691,8 +1692,8 @@ static int pick_link(struct nameidata *nd, struct path *link, last = nd->stack + nd->depth++; last->link = *link; - last->cookie = NULL; - last->inode = inode; + clear_delayed_call(&last->done); + nd->link_inode = inode; last->seq = seq; return 1; } @@ -4495,72 +4496,73 @@ EXPORT_SYMBOL(readlink_copy); /* * A helper for ->readlink(). This should be used *ONLY* for symlinks that - * have ->follow_link() touching nd only in nd_set_link(). Using (or not - * using) it for any given inode is up to filesystem. + * have ->get_link() not calling nd_jump_link(). Using (or not using) it + * for any given inode is up to filesystem. */ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - void *cookie; + DEFINE_DELAYED_CALL(done); struct inode *inode = d_inode(dentry); const char *link = inode->i_link; int res; if (!link) { - link = inode->i_op->follow_link(dentry, &cookie); + link = inode->i_op->get_link(dentry, inode, &done); if (IS_ERR(link)) return PTR_ERR(link); } res = readlink_copy(buffer, buflen, link); - if (inode->i_op->put_link) - inode->i_op->put_link(inode, cookie); + do_delayed_call(&done); return res; } EXPORT_SYMBOL(generic_readlink); /* get the link contents into pagecache */ -static char *page_getlink(struct dentry * dentry, struct page **ppage) +const char *page_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *callback) { char *kaddr; struct page *page; - struct address_space *mapping = dentry->d_inode->i_mapping; - page = read_mapping_page(mapping, 0, NULL); - if (IS_ERR(page)) - return (char*)page; - *ppage = page; - kaddr = kmap(page); - nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1); + struct address_space *mapping = inode->i_mapping; + + if (!dentry) { + page = find_get_page(mapping, 0); + if (!page) + return ERR_PTR(-ECHILD); + if (!PageUptodate(page)) { + put_page(page); + return ERR_PTR(-ECHILD); + } + } else { + page = read_mapping_page(mapping, 0, NULL); + if (IS_ERR(page)) + return (char*)page; + } + set_delayed_call(callback, page_put_link, page); + BUG_ON(mapping_gfp_mask(mapping) & __GFP_HIGHMEM); + kaddr = page_address(page); + nd_terminate_link(kaddr, inode->i_size, PAGE_SIZE - 1); return kaddr; } -int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) -{ - struct page *page = NULL; - int res = readlink_copy(buffer, buflen, page_getlink(dentry, &page)); - if (page) { - kunmap(page); - page_cache_release(page); - } - return res; -} -EXPORT_SYMBOL(page_readlink); +EXPORT_SYMBOL(page_get_link); -const char *page_follow_link_light(struct dentry *dentry, void **cookie) +void page_put_link(void *arg) { - struct page *page = NULL; - char *res = page_getlink(dentry, &page); - if (!IS_ERR(res)) - *cookie = page; - return res; + put_page(arg); } -EXPORT_SYMBOL(page_follow_link_light); +EXPORT_SYMBOL(page_put_link); -void page_put_link(struct inode *unused, void *cookie) +int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - struct page *page = cookie; - kunmap(page); - page_cache_release(page); + DEFINE_DELAYED_CALL(done); + int res = readlink_copy(buffer, buflen, + page_get_link(dentry, d_inode(dentry), + &done)); + do_delayed_call(&done); + return res; } -EXPORT_SYMBOL(page_put_link); +EXPORT_SYMBOL(page_readlink); /* * The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS @@ -4571,7 +4573,6 @@ int __page_symlink(struct inode *inode, const char *symname, int len, int nofs) struct page *page; void *fsdata; int err; - char *kaddr; unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE; if (nofs) flags |= AOP_FLAG_NOFS; @@ -4582,9 +4583,7 @@ retry: if (err) goto fail; - kaddr = kmap_atomic(page); - memcpy(kaddr, symname, len-1); - kunmap_atomic(kaddr); + memcpy(page_address(page), symname, len-1); err = pagecache_write_end(NULL, mapping, 0, len-1, len-1, page, fsdata); @@ -4609,7 +4608,6 @@ EXPORT_SYMBOL(page_symlink); const struct inode_operations page_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, }; EXPORT_SYMBOL(page_symlink_inode_operations); diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 9605a2f63549..ce1eb3f9dfe8 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -244,8 +244,7 @@ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo) #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) static const struct inode_operations ncp_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .setattr = ncp_notify_change, }; #endif @@ -283,6 +282,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &ncp_symlink_inode_operations; + inode_nohighmem(inode); inode->i_data.a_ops = &ncp_symlink_aops; #endif } else { diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c7e8b87da5b2..bdb4dc7b4ecd 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -408,9 +408,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st inode->i_fop = NULL; inode->i_flags |= S_AUTOMOUNT; } - } else if (S_ISLNK(inode->i_mode)) + } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &nfs_symlink_inode_operations; - else + inode_nohighmem(inode); + } else init_special_inode(inode, inode->i_mode, fattr->rdev); memset(&inode->i_atime, 0, sizeof(inode->i_atime)); @@ -1086,6 +1087,27 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode) || NFS_STALE(inode); } +int nfs_revalidate_mapping_rcu(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + unsigned long *bitlock = &nfsi->flags; + int ret = 0; + + if (IS_SWAPFILE(inode)) + goto out; + if (nfs_mapping_need_revalidate_inode(inode)) { + ret = -ECHILD; + goto out; + } + spin_lock(&inode->i_lock); + if (test_bit(NFS_INO_INVALIDATING, bitlock) || + (nfsi->cache_validity & NFS_INO_INVALID_DATA)) + ret = -ECHILD; + spin_unlock(&inode->i_lock); +out: + return ret; +} + /** * __nfs_revalidate_mapping - Revalidate the pagecache * @inode - pointer to host inode diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index b6de433da5db..4fe3eead3868 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -42,21 +42,35 @@ error: return -EIO; } -static const char *nfs_follow_link(struct dentry *dentry, void **cookie) +static const char *nfs_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct inode *inode = d_inode(dentry); struct page *page; void *err; - err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); - if (err) - return err; - page = read_cache_page(&inode->i_data, 0, - (filler_t *)nfs_symlink_filler, inode); - if (IS_ERR(page)) - return ERR_CAST(page); - *cookie = page; - return kmap(page); + if (!dentry) { + err = ERR_PTR(nfs_revalidate_mapping_rcu(inode)); + if (err) + return err; + page = find_get_page(inode->i_mapping, 0); + if (!page) + return ERR_PTR(-ECHILD); + if (!PageUptodate(page)) { + put_page(page); + return ERR_PTR(-ECHILD); + } + } else { + err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); + if (err) + return err; + page = read_cache_page(&inode->i_data, 0, + (filler_t *)nfs_symlink_filler, inode); + if (IS_ERR(page)) + return ERR_CAST(page); + } + set_delayed_call(done, page_put_link, page); + return page_address(page); } /* @@ -64,8 +78,7 @@ static const char *nfs_follow_link(struct dentry *dentry, void **cookie) */ const struct inode_operations nfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = nfs_follow_link, - .put_link = page_put_link, + .get_link = nfs_get_link, .getattr = nfs_getattr, .setattr = nfs_setattr, }; diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index ac2f64943ff4..10b22527a617 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -510,6 +510,7 @@ static int __nilfs_read_inode(struct super_block *sb, inode->i_mapping->a_ops = &nilfs_aops; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &nilfs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &nilfs_aops; } else { inode->i_op = &nilfs_special_inode_operations; diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index c9a1a491aa91..7ccdb961eea9 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -161,6 +161,7 @@ static int nilfs_symlink(struct inode *dir, struct dentry *dentry, /* slow symlink */ inode->i_op = &nilfs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &nilfs_aops; err = page_symlink(inode, symname, l); if (err) @@ -568,8 +569,7 @@ const struct inode_operations nilfs_special_inode_operations = { const struct inode_operations nilfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .permission = nilfs_permission, }; diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 8f87e05ee25d..97a563bab9a8 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -361,6 +361,7 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, break; case S_IFLNK: inode->i_op = &ocfs2_symlink_inode_operations; + inode_nohighmem(inode); i_size_write(inode, le64_to_cpu(fe->i_size)); break; default: diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 3123408da935..afb81eae2c18 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -1958,6 +1958,7 @@ static int ocfs2_symlink(struct inode *dir, inode->i_rdev = 0; newsize = l - 1; inode->i_op = &ocfs2_symlink_inode_operations; + inode_nohighmem(inode); if (l > ocfs2_fast_symlink_chars(sb)) { u32 offset = 0; diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index 66edce7ecfd7..6c2a3e3c521c 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c @@ -88,8 +88,7 @@ const struct address_space_operations ocfs2_fast_symlink_aops = { const struct inode_operations ocfs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, .setxattr = generic_setxattr, diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 4060ffde8722..964a60fa7afc 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -131,57 +131,23 @@ out_dput: return err; } - -struct ovl_link_data { - struct dentry *realdentry; - void *cookie; -}; - -static const char *ovl_follow_link(struct dentry *dentry, void **cookie) +static const char *ovl_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { struct dentry *realdentry; struct inode *realinode; - struct ovl_link_data *data = NULL; - const char *ret; + + if (!dentry) + return ERR_PTR(-ECHILD); realdentry = ovl_dentry_real(dentry); realinode = realdentry->d_inode; - if (WARN_ON(!realinode->i_op->follow_link)) + if (WARN_ON(!realinode->i_op->get_link)) return ERR_PTR(-EPERM); - if (realinode->i_op->put_link) { - data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - data->realdentry = realdentry; - } - - ret = realinode->i_op->follow_link(realdentry, cookie); - if (IS_ERR_OR_NULL(ret)) { - kfree(data); - return ret; - } - - if (data) - data->cookie = *cookie; - - *cookie = data; - - return ret; -} - -static void ovl_put_link(struct inode *unused, void *c) -{ - struct inode *realinode; - struct ovl_link_data *data = c; - - if (!data) - return; - - realinode = data->realdentry->d_inode; - realinode->i_op->put_link(realinode, data->cookie); - kfree(data); + return realinode->i_op->get_link(realdentry, realinode, done); } static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) @@ -378,8 +344,7 @@ static const struct inode_operations ovl_file_inode_operations = { static const struct inode_operations ovl_symlink_inode_operations = { .setattr = ovl_setattr, - .follow_link = ovl_follow_link, - .put_link = ovl_put_link, + .get_link = ovl_get_link, .readlink = ovl_readlink, .getattr = ovl_getattr, .setxattr = ovl_setxattr, diff --git a/fs/proc/base.c b/fs/proc/base.c index 4bd5d3118acd..55e01f88eac9 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1564,12 +1564,16 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) return -ENOENT; } -static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_pid_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct inode *inode = d_inode(dentry); struct path path; int error = -EACCES; + if (!dentry) + return ERR_PTR(-ECHILD); + /* Are we allowed to snoop on the tasks file descriptors? */ if (!proc_fd_access_allowed(inode)) goto out; @@ -1630,7 +1634,7 @@ out: const struct inode_operations proc_pid_link_inode_operations = { .readlink = proc_pid_readlink, - .follow_link = proc_pid_follow_link, + .get_link = proc_pid_get_link, .setattr = proc_setattr, }; @@ -1895,7 +1899,7 @@ static const struct dentry_operations tid_map_files_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int proc_map_files_get_link(struct dentry *dentry, struct path *path) +static int map_files_get_link(struct dentry *dentry, struct path *path) { unsigned long vm_start, vm_end; struct vm_area_struct *vma; @@ -1945,20 +1949,22 @@ struct map_files_info { * path to the file in question. */ static const char * -proc_map_files_follow_link(struct dentry *dentry, void **cookie) +proc_map_files_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { if (!capable(CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); - return proc_pid_follow_link(dentry, NULL); + return proc_pid_get_link(dentry, inode, done); } /* - * Identical to proc_pid_link_inode_operations except for follow_link() + * Identical to proc_pid_link_inode_operations except for get_link() */ static const struct inode_operations proc_map_files_link_inode_operations = { .readlink = proc_pid_readlink, - .follow_link = proc_map_files_follow_link, + .get_link = proc_map_files_get_link, .setattr = proc_setattr, }; @@ -1975,7 +1981,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry, return -ENOENT; ei = PROC_I(inode); - ei->op.proc_get_link = proc_map_files_get_link; + ei->op.proc_get_link = map_files_get_link; inode->i_op = &proc_map_files_link_inode_operations; inode->i_size = 64; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index bd95b9fdebb0..d0e9b9b6223e 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -393,24 +393,25 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif -static const char *proc_follow_link(struct dentry *dentry, void **cookie) +static void proc_put_link(void *p) { - struct proc_dir_entry *pde = PDE(d_inode(dentry)); - if (unlikely(!use_pde(pde))) - return ERR_PTR(-EINVAL); - *cookie = pde; - return pde->data; + unuse_pde(p); } -static void proc_put_link(struct inode *unused, void *p) +static const char *proc_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - unuse_pde(p); + struct proc_dir_entry *pde = PDE(inode); + if (unlikely(!use_pde(pde))) + return ERR_PTR(-EINVAL); + set_delayed_call(done, proc_put_link, pde); + return pde->data; } const struct inode_operations proc_link_inode_operations = { .readlink = generic_readlink, - .follow_link = proc_follow_link, - .put_link = proc_put_link, + .get_link = proc_get_link, }; struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index f6e8354b8cea..1dece8781f91 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -30,14 +30,18 @@ static const struct proc_ns_operations *ns_entries[] = { &mntns_operations, }; -static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_ns_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct inode *inode = d_inode(dentry); const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; struct task_struct *task; struct path ns_path; void *error = ERR_PTR(-EACCES); + if (!dentry) + return ERR_PTR(-ECHILD); + task = get_proc_task(inode); if (!task) return error; @@ -74,7 +78,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl static const struct inode_operations proc_ns_link_inode_operations = { .readlink = proc_ns_readlink, - .follow_link = proc_ns_follow_link, + .get_link = proc_ns_get_link, .setattr = proc_setattr, }; diff --git a/fs/proc/self.c b/fs/proc/self.c index 113b8d061fc0..67e8db442cf0 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c @@ -18,26 +18,28 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static const char *proc_self_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_self_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct pid_namespace *ns = dentry->d_sb->s_fs_info; + struct pid_namespace *ns = inode->i_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); char *name; if (!tgid) return ERR_PTR(-ENOENT); /* 11 for max length of signed int in decimal + NULL term */ - name = kmalloc(12, GFP_KERNEL); - if (!name) - return ERR_PTR(-ENOMEM); + name = kmalloc(12, dentry ? GFP_KERNEL : GFP_ATOMIC); + if (unlikely(!name)) + return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD); sprintf(name, "%d", tgid); - return *cookie = name; + set_delayed_call(done, kfree_link, name); + return name; } static const struct inode_operations proc_self_inode_operations = { .readlink = proc_self_readlink, - .follow_link = proc_self_follow_link, - .put_link = kfree_put_link, + .get_link = proc_self_get_link, }; static unsigned self_inum; diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c index 947b0f4fd0a1..9eacd59e0360 100644 --- a/fs/proc/thread_self.c +++ b/fs/proc/thread_self.c @@ -19,26 +19,29 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_thread_self_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { - struct pid_namespace *ns = dentry->d_sb->s_fs_info; + struct pid_namespace *ns = inode->i_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); pid_t pid = task_pid_nr_ns(current, ns); char *name; if (!pid) return ERR_PTR(-ENOENT); - name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); - if (!name) - return ERR_PTR(-ENOMEM); + name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, + dentry ? GFP_KERNEL : GFP_ATOMIC); + if (unlikely(!name)) + return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD); sprintf(name, "%d/task/%d", tgid, pid); - return *cookie = name; + set_delayed_call(done, kfree_link, name); + return name; } static const struct inode_operations proc_thread_self_inode_operations = { .readlink = proc_thread_self_readlink, - .follow_link = proc_thread_self_follow_link, - .put_link = kfree_put_link, + .get_link = proc_thread_self_get_link, }; static unsigned thread_self_inum; diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index c4bcb778886e..f37b3deb01b4 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -316,6 +316,7 @@ struct inode *qnx4_iget(struct super_block *sb, unsigned long ino) inode->i_fop = &qnx4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; } else { diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c index 32d2e1a9774c..9728b5499e1d 100644 --- a/fs/qnx6/inode.c +++ b/fs/qnx6/inode.c @@ -582,6 +582,7 @@ struct inode *qnx6_iget(struct super_block *sb, unsigned ino) inode->i_mapping->a_ops = &qnx6_aops; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &qnx6_aops; } else init_special_inode(inode, inode->i_mode, 0); diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 889d558b4e05..38981b037524 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -79,6 +79,7 @@ struct inode *ramfs_get_inode(struct super_block *sb, break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); break; } } diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 3d8e7e671d5b..ae9e5b308cf9 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1361,6 +1361,7 @@ static void init_inode(struct inode *inode, struct treepath *path) inode->i_fop = &reiserfs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &reiserfs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &reiserfs_address_space_operations; } else { inode->i_blocks = 0; diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index 47f96988fdd4..2a12d46d7fb4 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -1170,6 +1170,7 @@ static int reiserfs_symlink(struct inode *parent_dir, reiserfs_update_inode_transaction(parent_dir); inode->i_op = &reiserfs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &reiserfs_address_space_operations; retval = reiserfs_add_entry(&th, parent_dir, dentry->d_name.name, @@ -1664,8 +1665,7 @@ const struct inode_operations reiserfs_dir_inode_operations = { */ const struct inode_operations reiserfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .setattr = reiserfs_setattr, .setxattr = reiserfs_setxattr, .getxattr = reiserfs_getxattr, diff --git a/fs/romfs/super.c b/fs/romfs/super.c index 268733cda397..bb894e78a821 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -360,6 +360,7 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos) break; case ROMFH_SYM: i->i_op = &page_symlink_inode_operations; + inode_nohighmem(i); i->i_data.a_ops = &romfs_aops; mode |= S_IRWXUGO; break; diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c index a1ce5ce60632..0927b1e80ab6 100644 --- a/fs/squashfs/inode.c +++ b/fs/squashfs/inode.c @@ -41,6 +41,7 @@ #include <linux/fs.h> #include <linux/vfs.h> #include <linux/xattr.h> +#include <linux/pagemap.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -291,6 +292,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); inode->i_size = le32_to_cpu(sqsh_ino->symlink_size); inode->i_op = &squashfs_symlink_inode_ops; + inode_nohighmem(inode); inode->i_data.a_ops = &squashfs_symlink_aops; inode->i_mode |= S_IFLNK; squashfs_i(inode)->start = block; diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c index 12806dffb345..dbcc2f54bad4 100644 --- a/fs/squashfs/symlink.c +++ b/fs/squashfs/symlink.c @@ -119,8 +119,7 @@ const struct address_space_operations squashfs_symlink_aops = { const struct inode_operations squashfs_symlink_inode_ops = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .getxattr = generic_getxattr, .listxattr = squashfs_listxattr }; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 02fa1dcc5969..07ac18c355e7 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -146,8 +146,7 @@ static inline void write3byte(struct sysv_sb_info *sbi, static const struct inode_operations sysv_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, + .get_link = page_get_link, .getattr = sysv_getattr, }; @@ -163,6 +162,7 @@ void sysv_set_inode(struct inode *inode, dev_t rdev) inode->i_mapping->a_ops = &sysv_aops; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &sysv_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &sysv_aops; } else init_special_inode(inode, inode->i_mode, rdev); diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 0edc12856147..eff62801acbf 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1608,7 +1608,7 @@ const struct inode_operations ubifs_file_inode_operations = { const struct inode_operations ubifs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = ubifs_setxattr, diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 8d0b3ade0ff0..055746350d16 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1540,7 +1540,8 @@ reread: break; case ICBTAG_FILE_TYPE_SYMLINK: inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &udf_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mode = S_IFLNK | S_IRWXUGO; break; case ICBTAG_FILE_TYPE_MAIN: diff --git a/fs/udf/namei.c b/fs/udf/namei.c index c97b5a8d1e24..42eafb91f7ff 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -921,7 +921,8 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, } inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &udf_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { struct kernel_lb_addr eloc; @@ -1344,8 +1345,3 @@ const struct inode_operations udf_dir_inode_operations = { .rename = udf_rename, .tmpfile = udf_tmpfile, }; -const struct inode_operations udf_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -}; diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 862535b3ba58..8d619773056b 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -107,7 +107,7 @@ static int udf_symlink_filler(struct file *file, struct page *page) struct buffer_head *bh = NULL; unsigned char *symlink; int err; - unsigned char *p = kmap(page); + unsigned char *p = page_address(page); struct udf_inode_info *iinfo; uint32_t pos; @@ -141,7 +141,6 @@ static int udf_symlink_filler(struct file *file, struct page *page) up_read(&iinfo->i_data_sem); SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; @@ -149,7 +148,6 @@ out_unlock_inode: up_read(&iinfo->i_data_sem); SetPageError(page); out_unmap: - kunmap(page); unlock_page(page); return err; } diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 47bb3f5ca360..ce169b49429d 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -85,7 +85,6 @@ extern const struct inode_operations udf_dir_inode_operations; extern const struct file_operations udf_dir_operations; extern const struct inode_operations udf_file_inode_operations; extern const struct file_operations udf_file_operations; -extern const struct inode_operations udf_symlink_inode_operations; extern const struct address_space_operations udf_aops; extern const struct address_space_operations udf_adinicb_aops; extern const struct address_space_operations udf_symlink_aops; diff --git a/fs/ufs/Makefile b/fs/ufs/Makefile index 392db25c0b56..ec4a6b49fa13 100644 --- a/fs/ufs/Makefile +++ b/fs/ufs/Makefile @@ -5,5 +5,5 @@ obj-$(CONFIG_UFS_FS) += ufs.o ufs-objs := balloc.o cylinder.o dir.o file.o ialloc.o inode.o \ - namei.o super.o symlink.o util.o + namei.o super.o util.o ccflags-$(CONFIG_UFS_DEBUG) += -DDEBUG diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index a064cf44b143..d897e169ab9c 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -528,11 +528,12 @@ static void ufs_set_inode_ops(struct inode *inode) inode->i_mapping->a_ops = &ufs_aops; } else if (S_ISLNK(inode->i_mode)) { if (!inode->i_blocks) { - inode->i_op = &ufs_fast_symlink_inode_operations; inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink; + inode->i_op = &simple_symlink_inode_operations; } else { - inode->i_op = &ufs_symlink_inode_operations; inode->i_mapping->a_ops = &ufs_aops; + inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); } } else init_special_inode(inode, inode->i_mode, diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 47966554317c..acf4a3b61b81 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -123,14 +123,15 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry, if (l > UFS_SB(sb)->s_uspi->s_maxsymlinklen) { /* slow symlink */ - inode->i_op = &ufs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &ufs_aops; err = page_symlink(inode, symname, l); if (err) goto out_fail; } else { /* fast symlink */ - inode->i_op = &ufs_fast_symlink_inode_operations; + inode->i_op = &simple_symlink_inode_operations; inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink; memcpy(inode->i_link, symname, l); inode->i_size = l-1; diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c deleted file mode 100644 index 874480bb43e9..000000000000 --- a/fs/ufs/symlink.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * linux/fs/ufs/symlink.c - * - * Only fast symlinks left here - the rest is done by generic code. AV, 1999 - * - * Copyright (C) 1998 - * Daniel Pirkl <daniel.pirkl@emai.cz> - * Charles University, Faculty of Mathematics and Physics - * - * from - * - * linux/fs/ext2/symlink.c - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * - * from - * - * linux/fs/minix/symlink.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * ext2 symlink handling code - */ - -#include "ufs_fs.h" -#include "ufs.h" - -const struct inode_operations ufs_fast_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = simple_follow_link, - .setattr = ufs_setattr, -}; - -const struct inode_operations ufs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, - .setattr = ufs_setattr, -}; diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h index 7da4aca868c0..c87f4c3fa9dd 100644 --- a/fs/ufs/ufs.h +++ b/fs/ufs/ufs.h @@ -136,10 +136,6 @@ extern __printf(3, 4) void ufs_panic(struct super_block *, const char *, const char *, ...); void ufs_mark_sb_dirty(struct super_block *sb); -/* symlink.c */ -extern const struct inode_operations ufs_fast_symlink_inode_operations; -extern const struct inode_operations ufs_symlink_inode_operations; - static inline struct ufs_sb_info *UFS_SB(struct super_block *sb) { return sb->s_fs_info; diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 245268a0cdf0..06eafafe636e 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -414,13 +414,17 @@ xfs_vn_rename( * uio is kmalloced for this reason... */ STATIC const char * -xfs_vn_follow_link( +xfs_vn_get_link( struct dentry *dentry, - void **cookie) + struct inode *inode, + struct delayed_call *done) { char *link; int error = -ENOMEM; + if (!dentry) + return ERR_PTR(-ECHILD); + link = kmalloc(MAXPATHLEN+1, GFP_KERNEL); if (!link) goto out_err; @@ -429,7 +433,8 @@ xfs_vn_follow_link( if (unlikely(error)) goto out_kfree; - return *cookie = link; + set_delayed_call(done, kfree_link, link); + return link; out_kfree: kfree(link); @@ -1172,8 +1177,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = { static const struct inode_operations xfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = xfs_vn_follow_link, - .put_link = kfree_put_link, + .get_link = xfs_vn_get_link, .getattr = xfs_vn_getattr, .setattr = xfs_vn_setattr, .setxattr = generic_setxattr, |