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
This commit is contained in:
commit
32fb378437
@ -50,8 +50,7 @@ prototypes:
|
||||
int (*rename2) (struct inode *, struct dentry *,
|
||||
struct inode *, struct dentry *, unsigned int);
|
||||
int (*readlink) (struct dentry *, char __user *,int);
|
||||
const char *(*follow_link) (struct dentry *, void **);
|
||||
void (*put_link) (struct inode *, void *);
|
||||
const char *(*get_link) (struct dentry *, struct inode *, void **);
|
||||
void (*truncate) (struct inode *);
|
||||
int (*permission) (struct inode *, int, unsigned int);
|
||||
int (*get_acl)(struct inode *, int);
|
||||
@ -83,8 +82,7 @@ rmdir: yes (both) (see below)
|
||||
rename: yes (all) (see below)
|
||||
rename2: yes (all) (see below)
|
||||
readlink: no
|
||||
follow_link: no
|
||||
put_link: no
|
||||
get_link: no
|
||||
setattr: yes
|
||||
permission: no (may not block if called in rcu-walk mode)
|
||||
get_acl: no
|
||||
|
@ -504,3 +504,20 @@ 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.
|
||||
--
|
||||
[mandatory]
|
||||
->follow_link() is replaced with ->get_link(); same API, except that
|
||||
* ->get_link() gets inode as a separate argument
|
||||
* ->get_link() may be called in RCU mode - in that case NULL
|
||||
dentry is passed
|
||||
--
|
||||
[mandatory]
|
||||
->get_link() gets struct delayed_call *done now, and should do
|
||||
set_delayed_call() where it used to set *cookie.
|
||||
->put_link() is gone - just give the destructor to set_delayed_call()
|
||||
in ->get_link().
|
||||
|
@ -350,8 +350,8 @@ struct inode_operations {
|
||||
int (*rename2) (struct inode *, struct dentry *,
|
||||
struct inode *, struct dentry *, unsigned int);
|
||||
int (*readlink) (struct dentry *, char __user *,int);
|
||||
const char *(*follow_link) (struct dentry *, void **);
|
||||
void (*put_link) (struct inode *, void *);
|
||||
const char *(*get_link) (struct dentry *, struct inode *,
|
||||
struct delayed_call *);
|
||||
int (*permission) (struct inode *, int);
|
||||
int (*get_acl)(struct inode *, int);
|
||||
int (*setattr) (struct dentry *, struct iattr *);
|
||||
@ -434,20 +434,19 @@ otherwise noted.
|
||||
readlink: called by the readlink(2) system call. Only required if
|
||||
you want to support reading symbolic links
|
||||
|
||||
follow_link: called by the VFS to follow a symbolic link to the
|
||||
get_link: called by the VFS to follow a symbolic link to the
|
||||
inode it points to. Only required if you want to support
|
||||
symbolic links. This method returns the symlink body
|
||||
to traverse (and possibly resets the current position with
|
||||
nd_jump_link()). If the body won't go away until the inode
|
||||
is gone, nothing else is needed; if it needs to be otherwise
|
||||
pinned, the data needed to release whatever we'd grabbed
|
||||
is to be stored in void * variable passed by address to
|
||||
follow_link() instance.
|
||||
|
||||
put_link: called by the VFS to release resources allocated by
|
||||
follow_link(). The cookie stored by follow_link() is passed
|
||||
to this method as the last parameter; only called when
|
||||
cookie isn't NULL.
|
||||
pinned, arrange for its release by having get_link(..., ..., done)
|
||||
do set_delayed_call(done, destructor, argument).
|
||||
In that case destructor(argument) will be called once VFS is
|
||||
done with the body you've returned.
|
||||
May be called in RCU mode; that is indicated by NULL dentry
|
||||
argument. If request can't be handled without leaving RCU mode,
|
||||
have it return ERR_PTR(-ECHILD).
|
||||
|
||||
permission: called by the VFS to check for access rights on a POSIX-like
|
||||
filesystem.
|
||||
|
@ -118,12 +118,20 @@ failed:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const char *ll_follow_link(struct dentry *dentry, void **cookie)
|
||||
static void ll_put_link(void *p)
|
||||
{
|
||||
ptlrpc_req_finished(p);
|
||||
}
|
||||
|
||||
static const char *ll_get_link(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ptlrpc_request *request = NULL;
|
||||
int rc;
|
||||
char *symname = NULL;
|
||||
if (!dentry)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
CDEBUG(D_VFSTRACE, "VFS Op\n");
|
||||
ll_inode_size_lock(inode);
|
||||
@ -135,22 +143,16 @@ static const char *ll_follow_link(struct dentry *dentry, void **cookie)
|
||||
}
|
||||
|
||||
/* symname may contain a pointer to the request message buffer,
|
||||
* we delay request releasing until ll_put_link then.
|
||||
* we delay request releasing then.
|
||||
*/
|
||||
*cookie = request;
|
||||
set_delayed_call(done, ll_put_link, request);
|
||||
return symname;
|
||||
}
|
||||
|
||||
static void ll_put_link(struct inode *unused, void *cookie)
|
||||
{
|
||||
ptlrpc_req_finished(cookie);
|
||||
}
|
||||
|
||||
struct inode_operations ll_fast_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.setattr = ll_setattr,
|
||||
.follow_link = ll_follow_link,
|
||||
.put_link = ll_put_link,
|
||||
.get_link = ll_get_link,
|
||||
.getattr = ll_getattr,
|
||||
.permission = ll_inode_permission,
|
||||
.setxattr = ll_setxattr,
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 *);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
22
fs/libfs.c
22
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);
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
128
fs/namei.c
128
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,73 +4496,74 @@ 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;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(page_get_link);
|
||||
|
||||
void page_put_link(void *arg)
|
||||
{
|
||||
put_page(arg);
|
||||
}
|
||||
EXPORT_SYMBOL(page_put_link);
|
||||
|
||||
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);
|
||||
}
|
||||
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_readlink);
|
||||
|
||||
const char *page_follow_link_light(struct dentry *dentry, void **cookie)
|
||||
{
|
||||
struct page *page = NULL;
|
||||
char *res = page_getlink(dentry, &page);
|
||||
if (!IS_ERR(res))
|
||||
*cookie = page;
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(page_follow_link_light);
|
||||
|
||||
void page_put_link(struct inode *unused, void *cookie)
|
||||
{
|
||||
struct page *page = cookie;
|
||||
kunmap(page);
|
||||
page_cache_release(page);
|
||||
}
|
||||
EXPORT_SYMBOL(page_put_link);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
struct proc_dir_entry *pde = PDE(d_inode(dentry));
|
||||
if (unlikely(!use_pde(pde)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
*cookie = pde;
|
||||
return pde->data;
|
||||
}
|
||||
|
||||
static void proc_put_link(struct inode *unused, void *p)
|
||||
static void proc_put_link(void *p)
|
||||
{
|
||||
unuse_pde(p);
|
||||
}
|
||||
|
||||
static const char *proc_get_link(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
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)
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
@ -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;
|
||||
|
@ -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,
|
||||
|
34
include/linux/delayed_call.h
Normal file
34
include/linux/delayed_call.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef _DELAYED_CALL_H
|
||||
#define _DELAYED_CALL_H
|
||||
|
||||
/*
|
||||
* Poor man's closures; I wish we could've done them sanely polymorphic,
|
||||
* but...
|
||||
*/
|
||||
|
||||
struct delayed_call {
|
||||
void (*fn)(void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
#define DEFINE_DELAYED_CALL(name) struct delayed_call name = {NULL, NULL}
|
||||
|
||||
/* I really wish we had closures with sane typechecking... */
|
||||
static inline void set_delayed_call(struct delayed_call *call,
|
||||
void (*fn)(void *), void *arg)
|
||||
{
|
||||
call->fn = fn;
|
||||
call->arg = arg;
|
||||
}
|
||||
|
||||
static inline void do_delayed_call(struct delayed_call *call)
|
||||
{
|
||||
if (call->fn)
|
||||
call->fn(call->arg);
|
||||
}
|
||||
|
||||
static inline void clear_delayed_call(struct delayed_call *call)
|
||||
{
|
||||
call->fn = NULL;
|
||||
}
|
||||
#endif
|
@ -31,6 +31,7 @@
|
||||
#include <linux/blk_types.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/percpu-rwsem.h>
|
||||
#include <linux/delayed_call.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <uapi/linux/fs.h>
|
||||
@ -1633,12 +1634,11 @@ struct file_operations {
|
||||
|
||||
struct inode_operations {
|
||||
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
|
||||
const char * (*follow_link) (struct dentry *, void **);
|
||||
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
|
||||
int (*permission) (struct inode *, int);
|
||||
struct posix_acl * (*get_acl)(struct inode *, int);
|
||||
|
||||
int (*readlink) (struct dentry *, char __user *,int);
|
||||
void (*put_link) (struct inode *, void *);
|
||||
|
||||
int (*create) (struct inode *,struct dentry *, umode_t, bool);
|
||||
int (*link) (struct dentry *,struct inode *,struct dentry *);
|
||||
@ -2736,14 +2736,14 @@ extern const struct file_operations generic_ro_fops;
|
||||
|
||||
extern int readlink_copy(char __user *, int, const char *);
|
||||
extern int page_readlink(struct dentry *, char __user *, int);
|
||||
extern const char *page_follow_link_light(struct dentry *, void **);
|
||||
extern void page_put_link(struct inode *, void *);
|
||||
extern const char *page_get_link(struct dentry *, struct inode *,
|
||||
struct delayed_call *);
|
||||
extern void page_put_link(void *);
|
||||
extern int __page_symlink(struct inode *inode, const char *symname, int len,
|
||||
int nofs);
|
||||
extern int page_symlink(struct inode *inode, const char *symname, int len);
|
||||
extern const struct inode_operations page_symlink_inode_operations;
|
||||
extern void kfree_put_link(struct inode *, void *);
|
||||
extern void free_page_put_link(struct inode *, void *);
|
||||
extern void kfree_link(void *);
|
||||
extern int generic_readlink(struct dentry *, char __user *, int);
|
||||
extern void generic_fillattr(struct inode *, struct kstat *);
|
||||
int vfs_getattr_nosec(struct path *path, struct kstat *stat);
|
||||
@ -2754,7 +2754,8 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes);
|
||||
void inode_sub_bytes(struct inode *inode, loff_t bytes);
|
||||
loff_t inode_get_bytes(struct inode *inode);
|
||||
void inode_set_bytes(struct inode *inode, loff_t bytes);
|
||||
const char *simple_follow_link(struct dentry *, void **);
|
||||
const char *simple_get_link(struct dentry *, struct inode *,
|
||||
struct delayed_call *);
|
||||
extern const struct inode_operations simple_symlink_inode_operations;
|
||||
|
||||
extern int iterate_dir(struct file *, struct dir_context *);
|
||||
@ -3023,5 +3024,6 @@ static inline bool dir_relax(struct inode *inode)
|
||||
}
|
||||
|
||||
extern bool path_noexec(const struct path *path);
|
||||
extern void inode_nohighmem(struct inode *inode);
|
||||
|
||||
#endif /* _LINUX_FS_H */
|
||||
|
@ -359,6 +359,7 @@ extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
|
||||
extern int nfs_revalidate_inode_rcu(struct nfs_server *server, struct inode *inode);
|
||||
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
|
||||
extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
|
||||
extern int nfs_revalidate_mapping_rcu(struct inode *inode);
|
||||
extern int nfs_revalidate_mapping_protected(struct inode *inode, struct address_space *mapping);
|
||||
extern int nfs_setattr(struct dentry *, struct iattr *);
|
||||
extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr, struct nfs_fattr *);
|
||||
|
48
mm/shmem.c
48
mm/shmem.c
@ -2438,7 +2438,6 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
|
||||
int len;
|
||||
struct inode *inode;
|
||||
struct page *page;
|
||||
char *kaddr;
|
||||
struct shmem_inode_info *info;
|
||||
|
||||
len = strlen(symname) + 1;
|
||||
@ -2477,9 +2476,8 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
|
||||
}
|
||||
inode->i_mapping->a_ops = &shmem_aops;
|
||||
inode->i_op = &shmem_symlink_inode_operations;
|
||||
kaddr = kmap_atomic(page);
|
||||
memcpy(kaddr, symname, len);
|
||||
kunmap_atomic(kaddr);
|
||||
inode_nohighmem(inode);
|
||||
memcpy(page_address(page), symname, len);
|
||||
SetPageUptodate(page);
|
||||
set_page_dirty(page);
|
||||
unlock_page(page);
|
||||
@ -2492,23 +2490,34 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *shmem_follow_link(struct dentry *dentry, void **cookie)
|
||||
static void shmem_put_link(void *arg)
|
||||
{
|
||||
struct page *page = NULL;
|
||||
int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
unlock_page(page);
|
||||
*cookie = page;
|
||||
return kmap(page);
|
||||
mark_page_accessed(arg);
|
||||
put_page(arg);
|
||||
}
|
||||
|
||||
static void shmem_put_link(struct inode *unused, void *cookie)
|
||||
static const char *shmem_get_link(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
struct page *page = cookie;
|
||||
kunmap(page);
|
||||
mark_page_accessed(page);
|
||||
page_cache_release(page);
|
||||
struct page *page = NULL;
|
||||
int error;
|
||||
if (!dentry) {
|
||||
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 {
|
||||
error = shmem_getpage(inode, 0, &page, SGP_READ, NULL);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
unlock_page(page);
|
||||
}
|
||||
set_delayed_call(done, shmem_put_link, page);
|
||||
return page_address(page);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TMPFS_XATTR
|
||||
@ -2653,7 +2662,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
|
||||
static const struct inode_operations shmem_short_symlink_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = simple_follow_link,
|
||||
.get_link = simple_get_link,
|
||||
#ifdef CONFIG_TMPFS_XATTR
|
||||
.setxattr = shmem_setxattr,
|
||||
.getxattr = shmem_getxattr,
|
||||
@ -2664,8 +2673,7 @@ static const struct inode_operations shmem_short_symlink_operations = {
|
||||
|
||||
static const struct inode_operations shmem_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = shmem_follow_link,
|
||||
.put_link = shmem_put_link,
|
||||
.get_link = shmem_get_link,
|
||||
#ifdef CONFIG_TMPFS_XATTR
|
||||
.setxattr = shmem_setxattr,
|
||||
.getxattr = shmem_getxattr,
|
||||
|
Loading…
Reference in New Issue
Block a user