mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull VFS updates from Al Viro,
Misc cleanups all over the place, mainly wrt /proc interfaces (switch
create_proc_entry to proc_create(), get rid of the deprecated
create_proc_read_entry() in favor of using proc_create_data() and
seq_file etc).
7kloc removed.
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (204 commits)
don't bother with deferred freeing of fdtables
proc: Move non-public stuff from linux/proc_fs.h to fs/proc/internal.h
proc: Make the PROC_I() and PDE() macros internal to procfs
proc: Supply a function to remove a proc entry by PDE
take cgroup_open() and cpuset_open() to fs/proc/base.c
ppc: Clean up scanlog
ppc: Clean up rtas_flash driver somewhat
hostap: proc: Use remove_proc_subtree()
drm: proc: Use remove_proc_subtree()
drm: proc: Use minor->index to label things, not PDE->name
drm: Constify drm_proc_list[]
zoran: Don't print proc_dir_entry data in debug
reiserfs: Don't access the proc_dir_entry in r_open(), r_start() r_show()
proc: Supply an accessor for getting the data from a PDE's parent
airo: Use remove_proc_subtree()
rtl8192u: Don't need to save device proc dir PDE
rtl8187se: Use a dir under /proc/net/r8180/
proc: Add proc_mkdir_data()
proc: Move some bits from linux/proc_fs.h to linux/{of.h,signal.h,tty.h}
proc: Move PDE_NET() to fs/proc/proc_net.c
...
This commit is contained in:
commit
20b4fb4852
385 changed files with 7396 additions and 14540 deletions
|
|
@ -405,6 +405,37 @@ static const struct file_operations proc_lstats_operations = {
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CGROUPS
|
||||
static int cgroup_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pid *pid = PROC_I(inode)->pid;
|
||||
return single_open(file, proc_cgroup_show, pid);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_cgroup_operations = {
|
||||
.open = cgroup_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PROC_PID_CPUSET
|
||||
|
||||
static int cpuset_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pid *pid = PROC_I(inode)->pid;
|
||||
return single_open(file, proc_cpuset_show, pid);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_cpuset_operations = {
|
||||
.open = cpuset_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int proc_oom_score(struct task_struct *task, char *buffer)
|
||||
{
|
||||
unsigned long totalpages = totalram_pages + total_swap_pages;
|
||||
|
|
@ -1621,6 +1652,15 @@ int pid_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int pid_delete_dentry(const struct dentry *dentry)
|
||||
{
|
||||
/* Is the task we represent dead?
|
||||
* If so, then don't put the dentry on the lru list,
|
||||
* kill it immediately.
|
||||
*/
|
||||
return !proc_pid(dentry->d_inode)->tasks[PIDTYPE_PID].first;
|
||||
}
|
||||
|
||||
const struct dentry_operations pid_dentry_operations =
|
||||
{
|
||||
.d_revalidate = pid_revalidate,
|
||||
|
|
@ -2893,7 +2933,7 @@ retry:
|
|||
return iter;
|
||||
}
|
||||
|
||||
#define TGID_OFFSET (FIRST_PROCESS_ENTRY)
|
||||
#define TGID_OFFSET (FIRST_PROCESS_ENTRY + 1)
|
||||
|
||||
static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
|
||||
struct tgid_iter iter)
|
||||
|
|
@ -2916,13 +2956,21 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
|
|||
struct tgid_iter iter;
|
||||
struct pid_namespace *ns;
|
||||
filldir_t __filldir;
|
||||
loff_t pos = filp->f_pos;
|
||||
|
||||
if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET)
|
||||
if (pos >= PID_MAX_LIMIT + TGID_OFFSET)
|
||||
goto out;
|
||||
|
||||
ns = filp->f_dentry->d_sb->s_fs_info;
|
||||
if (pos == TGID_OFFSET - 1) {
|
||||
if (proc_fill_cache(filp, dirent, filldir, "self", 4,
|
||||
NULL, NULL, NULL) < 0)
|
||||
goto out;
|
||||
iter.tgid = 0;
|
||||
} else {
|
||||
iter.tgid = pos - TGID_OFFSET;
|
||||
}
|
||||
iter.task = NULL;
|
||||
iter.tgid = filp->f_pos - TGID_OFFSET;
|
||||
ns = filp->f_dentry->d_sb->s_fs_info;
|
||||
for (iter = next_tgid(ns, iter);
|
||||
iter.task;
|
||||
iter.tgid += 1, iter = next_tgid(ns, iter)) {
|
||||
|
|
|
|||
|
|
@ -11,4 +11,9 @@ extern const struct inode_operations proc_fdinfo_inode_operations;
|
|||
|
||||
extern int proc_fd_permission(struct inode *inode, int mask);
|
||||
|
||||
static inline int proc_fd(struct inode *inode)
|
||||
{
|
||||
return PROC_I(inode)->fd;
|
||||
}
|
||||
|
||||
#endif /* __PROCFS_FD_H__ */
|
||||
|
|
|
|||
|
|
@ -36,212 +36,6 @@ static int proc_match(unsigned int len, const char *name, struct proc_dir_entry
|
|||
return !memcmp(name, de->name, len);
|
||||
}
|
||||
|
||||
/* buffer size is one page but our output routines use some slack for overruns */
|
||||
#define PROC_BLOCK_SIZE (PAGE_SIZE - 1024)
|
||||
|
||||
static ssize_t
|
||||
__proc_file_read(struct file *file, char __user *buf, size_t nbytes,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct inode * inode = file_inode(file);
|
||||
char *page;
|
||||
ssize_t retval=0;
|
||||
int eof=0;
|
||||
ssize_t n, count;
|
||||
char *start;
|
||||
struct proc_dir_entry * dp;
|
||||
unsigned long long pos;
|
||||
|
||||
/*
|
||||
* Gaah, please just use "seq_file" instead. The legacy /proc
|
||||
* interfaces cut loff_t down to off_t for reads, and ignore
|
||||
* the offset entirely for writes..
|
||||
*/
|
||||
pos = *ppos;
|
||||
if (pos > MAX_NON_LFS)
|
||||
return 0;
|
||||
if (nbytes > MAX_NON_LFS - pos)
|
||||
nbytes = MAX_NON_LFS - pos;
|
||||
|
||||
dp = PDE(inode);
|
||||
if (!(page = (char*) __get_free_page(GFP_TEMPORARY)))
|
||||
return -ENOMEM;
|
||||
|
||||
while ((nbytes > 0) && !eof) {
|
||||
count = min_t(size_t, PROC_BLOCK_SIZE, nbytes);
|
||||
|
||||
start = NULL;
|
||||
if (dp->read_proc) {
|
||||
/*
|
||||
* How to be a proc read function
|
||||
* ------------------------------
|
||||
* Prototype:
|
||||
* int f(char *buffer, char **start, off_t offset,
|
||||
* int count, int *peof, void *dat)
|
||||
*
|
||||
* Assume that the buffer is "count" bytes in size.
|
||||
*
|
||||
* If you know you have supplied all the data you
|
||||
* have, set *peof.
|
||||
*
|
||||
* You have three ways to return data:
|
||||
* 0) Leave *start = NULL. (This is the default.)
|
||||
* Put the data of the requested offset at that
|
||||
* offset within the buffer. Return the number (n)
|
||||
* of bytes there are from the beginning of the
|
||||
* buffer up to the last byte of data. If the
|
||||
* number of supplied bytes (= n - offset) is
|
||||
* greater than zero and you didn't signal eof
|
||||
* and the reader is prepared to take more data
|
||||
* you will be called again with the requested
|
||||
* offset advanced by the number of bytes
|
||||
* absorbed. This interface is useful for files
|
||||
* no larger than the buffer.
|
||||
* 1) Set *start = an unsigned long value less than
|
||||
* the buffer address but greater than zero.
|
||||
* Put the data of the requested offset at the
|
||||
* beginning of the buffer. Return the number of
|
||||
* bytes of data placed there. If this number is
|
||||
* greater than zero and you didn't signal eof
|
||||
* and the reader is prepared to take more data
|
||||
* you will be called again with the requested
|
||||
* offset advanced by *start. This interface is
|
||||
* useful when you have a large file consisting
|
||||
* of a series of blocks which you want to count
|
||||
* and return as wholes.
|
||||
* (Hack by Paul.Russell@rustcorp.com.au)
|
||||
* 2) Set *start = an address within the buffer.
|
||||
* Put the data of the requested offset at *start.
|
||||
* Return the number of bytes of data placed there.
|
||||
* If this number is greater than zero and you
|
||||
* didn't signal eof and the reader is prepared to
|
||||
* take more data you will be called again with the
|
||||
* requested offset advanced by the number of bytes
|
||||
* absorbed.
|
||||
*/
|
||||
n = dp->read_proc(page, &start, *ppos,
|
||||
count, &eof, dp->data);
|
||||
} else
|
||||
break;
|
||||
|
||||
if (n == 0) /* end of file */
|
||||
break;
|
||||
if (n < 0) { /* error */
|
||||
if (retval == 0)
|
||||
retval = n;
|
||||
break;
|
||||
}
|
||||
|
||||
if (start == NULL) {
|
||||
if (n > PAGE_SIZE) /* Apparent buffer overflow */
|
||||
n = PAGE_SIZE;
|
||||
n -= *ppos;
|
||||
if (n <= 0)
|
||||
break;
|
||||
if (n > count)
|
||||
n = count;
|
||||
start = page + *ppos;
|
||||
} else if (start < page) {
|
||||
if (n > PAGE_SIZE) /* Apparent buffer overflow */
|
||||
n = PAGE_SIZE;
|
||||
if (n > count) {
|
||||
/*
|
||||
* Don't reduce n because doing so might
|
||||
* cut off part of a data block.
|
||||
*/
|
||||
pr_warn("proc_file_read: count exceeded\n");
|
||||
}
|
||||
} else /* start >= page */ {
|
||||
unsigned long startoff = (unsigned long)(start - page);
|
||||
if (n > (PAGE_SIZE - startoff)) /* buffer overflow? */
|
||||
n = PAGE_SIZE - startoff;
|
||||
if (n > count)
|
||||
n = count;
|
||||
}
|
||||
|
||||
n -= copy_to_user(buf, start < page ? page : start, n);
|
||||
if (n == 0) {
|
||||
if (retval == 0)
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
*ppos += start < page ? (unsigned long)start : n;
|
||||
nbytes -= n;
|
||||
buf += n;
|
||||
retval += n;
|
||||
}
|
||||
free_page((unsigned long) page);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
proc_file_read(struct file *file, char __user *buf, size_t nbytes,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
ssize_t rv = -EIO;
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
}
|
||||
pde->pde_users++;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
rv = __proc_file_read(file, buf, nbytes, ppos);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
proc_file_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
ssize_t rv = -EIO;
|
||||
|
||||
if (pde->write_proc) {
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
}
|
||||
pde->pde_users++;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
/* FIXME: does this routine need ppos? probably... */
|
||||
rv = pde->write_proc(file, buffer, count, pde->data);
|
||||
pde_users_dec(pde);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static loff_t
|
||||
proc_file_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
loff_t retval = -EINVAL;
|
||||
switch (orig) {
|
||||
case 1:
|
||||
offset += file->f_pos;
|
||||
/* fallthrough */
|
||||
case 0:
|
||||
if (offset < 0 || offset > MAX_NON_LFS)
|
||||
break;
|
||||
file->f_pos = retval = offset;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct file_operations proc_file_operations = {
|
||||
.llseek = proc_file_lseek,
|
||||
.read = proc_file_read,
|
||||
.write = proc_file_write,
|
||||
};
|
||||
|
||||
static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
|
@ -371,7 +165,7 @@ void proc_free_inum(unsigned int inum)
|
|||
|
||||
static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
nd_set_link(nd, PDE(dentry->d_inode)->data);
|
||||
nd_set_link(nd, __PDE_DATA(dentry->d_inode));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -541,19 +335,17 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
|
|||
return ret;
|
||||
|
||||
if (S_ISDIR(dp->mode)) {
|
||||
if (dp->proc_iops == NULL) {
|
||||
dp->proc_fops = &proc_dir_operations;
|
||||
dp->proc_iops = &proc_dir_inode_operations;
|
||||
}
|
||||
dp->proc_fops = &proc_dir_operations;
|
||||
dp->proc_iops = &proc_dir_inode_operations;
|
||||
dir->nlink++;
|
||||
} else if (S_ISLNK(dp->mode)) {
|
||||
if (dp->proc_iops == NULL)
|
||||
dp->proc_iops = &proc_link_inode_operations;
|
||||
dp->proc_iops = &proc_link_inode_operations;
|
||||
} else if (S_ISREG(dp->mode)) {
|
||||
if (dp->proc_fops == NULL)
|
||||
dp->proc_fops = &proc_file_operations;
|
||||
if (dp->proc_iops == NULL)
|
||||
dp->proc_iops = &proc_file_inode_operations;
|
||||
BUG_ON(dp->proc_fops == NULL);
|
||||
dp->proc_iops = &proc_file_inode_operations;
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock(&proc_subdir_lock);
|
||||
|
|
@ -636,13 +428,17 @@ struct proc_dir_entry *proc_symlink(const char *name,
|
|||
}
|
||||
EXPORT_SYMBOL(proc_symlink);
|
||||
|
||||
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
|
||||
struct proc_dir_entry *parent)
|
||||
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
|
||||
struct proc_dir_entry *parent, void *data)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
|
||||
if (mode == 0)
|
||||
mode = S_IRUGO | S_IXUGO;
|
||||
|
||||
ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
|
||||
if (ent) {
|
||||
ent->data = data;
|
||||
if (proc_register(parent, ent) < 0) {
|
||||
kfree(ent);
|
||||
ent = NULL;
|
||||
|
|
@ -650,82 +446,39 @@ struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
|
|||
}
|
||||
return ent;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(proc_mkdir_data);
|
||||
|
||||
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
|
||||
struct proc_dir_entry *parent)
|
||||
{
|
||||
return proc_mkdir_data(name, mode, parent, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(proc_mkdir_mode);
|
||||
|
||||
struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
|
||||
struct proc_dir_entry *parent)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
|
||||
ent = __proc_create(&parent, name, S_IFDIR | S_IRUGO | S_IXUGO, 2);
|
||||
if (ent) {
|
||||
ent->data = net;
|
||||
if (proc_register(parent, ent) < 0) {
|
||||
kfree(ent);
|
||||
ent = NULL;
|
||||
}
|
||||
}
|
||||
return ent;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(proc_net_mkdir);
|
||||
|
||||
struct proc_dir_entry *proc_mkdir(const char *name,
|
||||
struct proc_dir_entry *parent)
|
||||
{
|
||||
return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
|
||||
return proc_mkdir_data(name, 0, parent, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(proc_mkdir);
|
||||
|
||||
struct proc_dir_entry *create_proc_entry(const char *name, umode_t mode,
|
||||
struct proc_dir_entry *parent)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
nlink_t nlink;
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
if ((mode & S_IALLUGO) == 0)
|
||||
mode |= S_IRUGO | S_IXUGO;
|
||||
nlink = 2;
|
||||
} else {
|
||||
if ((mode & S_IFMT) == 0)
|
||||
mode |= S_IFREG;
|
||||
if ((mode & S_IALLUGO) == 0)
|
||||
mode |= S_IRUGO;
|
||||
nlink = 1;
|
||||
}
|
||||
|
||||
ent = __proc_create(&parent, name, mode, nlink);
|
||||
if (ent) {
|
||||
if (proc_register(parent, ent) < 0) {
|
||||
kfree(ent);
|
||||
ent = NULL;
|
||||
}
|
||||
}
|
||||
return ent;
|
||||
}
|
||||
EXPORT_SYMBOL(create_proc_entry);
|
||||
|
||||
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
|
||||
struct proc_dir_entry *parent,
|
||||
const struct file_operations *proc_fops,
|
||||
void *data)
|
||||
{
|
||||
struct proc_dir_entry *pde;
|
||||
nlink_t nlink;
|
||||
if ((mode & S_IFMT) == 0)
|
||||
mode |= S_IFREG;
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
if ((mode & S_IALLUGO) == 0)
|
||||
mode |= S_IRUGO | S_IXUGO;
|
||||
nlink = 2;
|
||||
} else {
|
||||
if ((mode & S_IFMT) == 0)
|
||||
mode |= S_IFREG;
|
||||
if ((mode & S_IALLUGO) == 0)
|
||||
mode |= S_IRUGO;
|
||||
nlink = 1;
|
||||
if (!S_ISREG(mode)) {
|
||||
WARN_ON(1); /* use proc_mkdir() */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pde = __proc_create(&parent, name, mode, nlink);
|
||||
if ((mode & S_IALLUGO) == 0)
|
||||
mode |= S_IRUGO;
|
||||
pde = __proc_create(&parent, name, mode, 1);
|
||||
if (!pde)
|
||||
goto out;
|
||||
pde->proc_fops = proc_fops;
|
||||
|
|
@ -739,6 +492,19 @@ out:
|
|||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(proc_create_data);
|
||||
|
||||
void proc_set_size(struct proc_dir_entry *de, loff_t size)
|
||||
{
|
||||
de->size = size;
|
||||
}
|
||||
EXPORT_SYMBOL(proc_set_size);
|
||||
|
||||
void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid)
|
||||
{
|
||||
de->uid = uid;
|
||||
de->gid = gid;
|
||||
}
|
||||
EXPORT_SYMBOL(proc_set_user);
|
||||
|
||||
static void free_proc_entry(struct proc_dir_entry *de)
|
||||
{
|
||||
|
|
@ -755,41 +521,6 @@ void pde_put(struct proc_dir_entry *pde)
|
|||
free_proc_entry(pde);
|
||||
}
|
||||
|
||||
static void entry_rundown(struct proc_dir_entry *de)
|
||||
{
|
||||
spin_lock(&de->pde_unload_lock);
|
||||
/*
|
||||
* Stop accepting new callers into module. If you're
|
||||
* dynamically allocating ->proc_fops, save a pointer somewhere.
|
||||
*/
|
||||
de->proc_fops = NULL;
|
||||
/* Wait until all existing callers into module are done. */
|
||||
if (de->pde_users > 0) {
|
||||
DECLARE_COMPLETION_ONSTACK(c);
|
||||
|
||||
if (!de->pde_unload_completion)
|
||||
de->pde_unload_completion = &c;
|
||||
|
||||
spin_unlock(&de->pde_unload_lock);
|
||||
|
||||
wait_for_completion(de->pde_unload_completion);
|
||||
|
||||
spin_lock(&de->pde_unload_lock);
|
||||
}
|
||||
|
||||
while (!list_empty(&de->pde_openers)) {
|
||||
struct pde_opener *pdeo;
|
||||
|
||||
pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
|
||||
list_del(&pdeo->lh);
|
||||
spin_unlock(&de->pde_unload_lock);
|
||||
pdeo->release(pdeo->inode, pdeo->file);
|
||||
kfree(pdeo);
|
||||
spin_lock(&de->pde_unload_lock);
|
||||
}
|
||||
spin_unlock(&de->pde_unload_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a /proc entry and free it if it's not currently in use.
|
||||
*/
|
||||
|
|
@ -821,7 +552,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
|||
return;
|
||||
}
|
||||
|
||||
entry_rundown(de);
|
||||
proc_entry_rundown(de);
|
||||
|
||||
if (S_ISDIR(de->mode))
|
||||
parent->nlink--;
|
||||
|
|
@ -870,7 +601,7 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
|
|||
}
|
||||
spin_unlock(&proc_subdir_lock);
|
||||
|
||||
entry_rundown(de);
|
||||
proc_entry_rundown(de);
|
||||
next = de->parent;
|
||||
if (S_ISDIR(de->mode))
|
||||
next->nlink--;
|
||||
|
|
@ -886,3 +617,23 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
|
|||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(remove_proc_subtree);
|
||||
|
||||
void *proc_get_parent_data(const struct inode *inode)
|
||||
{
|
||||
struct proc_dir_entry *de = PDE(inode);
|
||||
return de->parent->data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(proc_get_parent_data);
|
||||
|
||||
void proc_remove(struct proc_dir_entry *de)
|
||||
{
|
||||
if (de)
|
||||
remove_proc_subtree(de->name, de->parent);
|
||||
}
|
||||
EXPORT_SYMBOL(proc_remove);
|
||||
|
||||
void *PDE_DATA(const struct inode *inode)
|
||||
{
|
||||
return __PDE_DATA(inode);
|
||||
}
|
||||
EXPORT_SYMBOL(PDE_DATA);
|
||||
|
|
|
|||
283
fs/proc/inode.c
283
fs/proc/inode.c
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/magic.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
|
@ -50,8 +51,8 @@ static void proc_evict_inode(struct inode *inode)
|
|||
sysctl_head_put(head);
|
||||
}
|
||||
/* Release any associated namespace */
|
||||
ns_ops = PROC_I(inode)->ns_ops;
|
||||
ns = PROC_I(inode)->ns;
|
||||
ns_ops = PROC_I(inode)->ns.ns_ops;
|
||||
ns = PROC_I(inode)->ns.ns;
|
||||
if (ns_ops && ns)
|
||||
ns_ops->put(ns);
|
||||
}
|
||||
|
|
@ -72,8 +73,8 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
|
|||
ei->pde = NULL;
|
||||
ei->sysctl = NULL;
|
||||
ei->sysctl_entry = NULL;
|
||||
ei->ns = NULL;
|
||||
ei->ns_ops = NULL;
|
||||
ei->ns.ns = NULL;
|
||||
ei->ns.ns_ops = NULL;
|
||||
inode = &ei->vfs_inode;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
||||
return inode;
|
||||
|
|
@ -129,96 +130,100 @@ static const struct super_operations proc_sops = {
|
|||
.show_options = proc_show_options,
|
||||
};
|
||||
|
||||
static void __pde_users_dec(struct proc_dir_entry *pde)
|
||||
enum {BIAS = -1U<<31};
|
||||
|
||||
static inline int use_pde(struct proc_dir_entry *pde)
|
||||
{
|
||||
pde->pde_users--;
|
||||
if (pde->pde_unload_completion && pde->pde_users == 0)
|
||||
return atomic_inc_unless_negative(&pde->in_use);
|
||||
}
|
||||
|
||||
static void unuse_pde(struct proc_dir_entry *pde)
|
||||
{
|
||||
if (atomic_dec_return(&pde->in_use) == BIAS)
|
||||
complete(pde->pde_unload_completion);
|
||||
}
|
||||
|
||||
void pde_users_dec(struct proc_dir_entry *pde)
|
||||
/* pde is locked */
|
||||
static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo)
|
||||
{
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
__pde_users_dec(pde);
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
if (pdeo->closing) {
|
||||
/* somebody else is doing that, just wait */
|
||||
DECLARE_COMPLETION_ONSTACK(c);
|
||||
pdeo->c = &c;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
wait_for_completion(&c);
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
} else {
|
||||
struct file *file;
|
||||
pdeo->closing = 1;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
file = pdeo->file;
|
||||
pde->proc_fops->release(file_inode(file), file);
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
list_del_init(&pdeo->lh);
|
||||
if (pdeo->c)
|
||||
complete(pdeo->c);
|
||||
kfree(pdeo);
|
||||
}
|
||||
}
|
||||
|
||||
void proc_entry_rundown(struct proc_dir_entry *de)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(c);
|
||||
/* Wait until all existing callers into module are done. */
|
||||
de->pde_unload_completion = &c;
|
||||
if (atomic_add_return(BIAS, &de->in_use) != BIAS)
|
||||
wait_for_completion(&c);
|
||||
|
||||
spin_lock(&de->pde_unload_lock);
|
||||
while (!list_empty(&de->pde_openers)) {
|
||||
struct pde_opener *pdeo;
|
||||
pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
|
||||
close_pdeo(de, pdeo);
|
||||
}
|
||||
spin_unlock(&de->pde_unload_lock);
|
||||
}
|
||||
|
||||
static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
loff_t rv = -EINVAL;
|
||||
loff_t (*llseek)(struct file *, loff_t, int);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
/*
|
||||
* remove_proc_entry() is going to delete PDE (as part of module
|
||||
* cleanup sequence). No new callers into module allowed.
|
||||
*/
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
if (use_pde(pde)) {
|
||||
loff_t (*llseek)(struct file *, loff_t, int);
|
||||
llseek = pde->proc_fops->llseek;
|
||||
if (!llseek)
|
||||
llseek = default_llseek;
|
||||
rv = llseek(file, offset, whence);
|
||||
unuse_pde(pde);
|
||||
}
|
||||
/*
|
||||
* Bump refcount so that remove_proc_entry will wail for ->llseek to
|
||||
* complete.
|
||||
*/
|
||||
pde->pde_users++;
|
||||
/*
|
||||
* Save function pointer under lock, to protect against ->proc_fops
|
||||
* NULL'ifying right after ->pde_unload_lock is dropped.
|
||||
*/
|
||||
llseek = pde->proc_fops->llseek;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (!llseek)
|
||||
llseek = default_llseek;
|
||||
rv = llseek(file, offset, whence);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
|
||||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
ssize_t rv = -EIO;
|
||||
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
if (use_pde(pde)) {
|
||||
read = pde->proc_fops->read;
|
||||
if (read)
|
||||
rv = read(file, buf, count, ppos);
|
||||
unuse_pde(pde);
|
||||
}
|
||||
pde->pde_users++;
|
||||
read = pde->proc_fops->read;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (read)
|
||||
rv = read(file, buf, count, ppos);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
|
||||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
ssize_t rv = -EIO;
|
||||
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
if (use_pde(pde)) {
|
||||
write = pde->proc_fops->write;
|
||||
if (write)
|
||||
rv = write(file, buf, count, ppos);
|
||||
unuse_pde(pde);
|
||||
}
|
||||
pde->pde_users++;
|
||||
write = pde->proc_fops->write;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (write)
|
||||
rv = write(file, buf, count, ppos);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
@ -227,20 +232,12 @@ static unsigned int proc_reg_poll(struct file *file, struct poll_table_struct *p
|
|||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
unsigned int rv = DEFAULT_POLLMASK;
|
||||
unsigned int (*poll)(struct file *, struct poll_table_struct *);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
if (use_pde(pde)) {
|
||||
poll = pde->proc_fops->poll;
|
||||
if (poll)
|
||||
rv = poll(file, pts);
|
||||
unuse_pde(pde);
|
||||
}
|
||||
pde->pde_users++;
|
||||
poll = pde->proc_fops->poll;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (poll)
|
||||
rv = poll(file, pts);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
@ -249,20 +246,12 @@ static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigne
|
|||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
long rv = -ENOTTY;
|
||||
long (*ioctl)(struct file *, unsigned int, unsigned long);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
if (use_pde(pde)) {
|
||||
ioctl = pde->proc_fops->unlocked_ioctl;
|
||||
if (ioctl)
|
||||
rv = ioctl(file, cmd, arg);
|
||||
unuse_pde(pde);
|
||||
}
|
||||
pde->pde_users++;
|
||||
ioctl = pde->proc_fops->unlocked_ioctl;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (ioctl)
|
||||
rv = ioctl(file, cmd, arg);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
@ -272,20 +261,12 @@ static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned
|
|||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
long rv = -ENOTTY;
|
||||
long (*compat_ioctl)(struct file *, unsigned int, unsigned long);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
if (use_pde(pde)) {
|
||||
compat_ioctl = pde->proc_fops->compat_ioctl;
|
||||
if (compat_ioctl)
|
||||
rv = compat_ioctl(file, cmd, arg);
|
||||
unuse_pde(pde);
|
||||
}
|
||||
pde->pde_users++;
|
||||
compat_ioctl = pde->proc_fops->compat_ioctl;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (compat_ioctl)
|
||||
rv = compat_ioctl(file, cmd, arg);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -295,20 +276,12 @@ static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma)
|
|||
struct proc_dir_entry *pde = PDE(file_inode(file));
|
||||
int rv = -EIO;
|
||||
int (*mmap)(struct file *, struct vm_area_struct *);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
if (use_pde(pde)) {
|
||||
mmap = pde->proc_fops->mmap;
|
||||
if (mmap)
|
||||
rv = mmap(file, vma);
|
||||
unuse_pde(pde);
|
||||
}
|
||||
pde->pde_users++;
|
||||
mmap = pde->proc_fops->mmap;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (mmap)
|
||||
rv = mmap(file, vma);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
@ -330,91 +303,47 @@ static int proc_reg_open(struct inode *inode, struct file *file)
|
|||
* by hand in remove_proc_entry(). For this, save opener's credentials
|
||||
* for later.
|
||||
*/
|
||||
pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL);
|
||||
pdeo = kzalloc(sizeof(struct pde_opener), GFP_KERNEL);
|
||||
if (!pdeo)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (!pde->proc_fops) {
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
if (!use_pde(pde)) {
|
||||
kfree(pdeo);
|
||||
return -ENOENT;
|
||||
}
|
||||
pde->pde_users++;
|
||||
open = pde->proc_fops->open;
|
||||
release = pde->proc_fops->release;
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (open)
|
||||
rv = open(inode, file);
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
if (rv == 0 && release) {
|
||||
/* To know what to release. */
|
||||
pdeo->inode = inode;
|
||||
pdeo->file = file;
|
||||
/* Strictly for "too late" ->release in proc_reg_release(). */
|
||||
pdeo->release = release;
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
list_add(&pdeo->lh, &pde->pde_openers);
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
} else
|
||||
kfree(pdeo);
|
||||
__pde_users_dec(pde);
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
unuse_pde(pde);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde,
|
||||
struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pde_opener *pdeo;
|
||||
|
||||
list_for_each_entry(pdeo, &pde->pde_openers, lh) {
|
||||
if (pdeo->inode == inode && pdeo->file == file)
|
||||
return pdeo;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int proc_reg_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct proc_dir_entry *pde = PDE(inode);
|
||||
int rv = 0;
|
||||
int (*release)(struct inode *, struct file *);
|
||||
struct pde_opener *pdeo;
|
||||
|
||||
spin_lock(&pde->pde_unload_lock);
|
||||
pdeo = find_pde_opener(pde, inode, file);
|
||||
if (!pde->proc_fops) {
|
||||
/*
|
||||
* Can't simply exit, __fput() will think that everything is OK,
|
||||
* and move on to freeing struct file. remove_proc_entry() will
|
||||
* find slacker in opener's list and will try to do non-trivial
|
||||
* things with struct file. Therefore, remove opener from list.
|
||||
*
|
||||
* But if opener is removed from list, who will ->release it?
|
||||
*/
|
||||
if (pdeo) {
|
||||
list_del(&pdeo->lh);
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
rv = pdeo->release(inode, file);
|
||||
kfree(pdeo);
|
||||
} else
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
return rv;
|
||||
}
|
||||
pde->pde_users++;
|
||||
release = pde->proc_fops->release;
|
||||
if (pdeo) {
|
||||
list_del(&pdeo->lh);
|
||||
kfree(pdeo);
|
||||
list_for_each_entry(pdeo, &pde->pde_openers, lh) {
|
||||
if (pdeo->file == file) {
|
||||
close_pdeo(pde, pdeo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&pde->pde_unload_lock);
|
||||
|
||||
if (release)
|
||||
rv = release(inode, file);
|
||||
|
||||
pde_users_dec(pde);
|
||||
return rv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations proc_reg_file_ops = {
|
||||
|
|
@ -462,8 +391,8 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
|
|||
inode->i_size = de->size;
|
||||
if (de->nlink)
|
||||
set_nlink(inode, de->nlink);
|
||||
if (de->proc_iops)
|
||||
inode->i_op = de->proc_iops;
|
||||
WARN_ON(!de->proc_iops);
|
||||
inode->i_op = de->proc_iops;
|
||||
if (de->proc_fops) {
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
|
@ -506,5 +435,5 @@ int proc_fill_super(struct super_block *s)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return proc_setup_self(s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* internal.h: internal procfs definitions
|
||||
/* Internal procfs definitions
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
|
|
@ -9,62 +9,83 @@
|
|||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/proc_ns.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/binfmts.h>
|
||||
struct ctl_table_header;
|
||||
struct mempolicy;
|
||||
|
||||
extern struct proc_dir_entry proc_root;
|
||||
extern void proc_self_init(void);
|
||||
#ifdef CONFIG_PROC_SYSCTL
|
||||
extern int proc_sys_init(void);
|
||||
extern void sysctl_head_put(struct ctl_table_header *head);
|
||||
#else
|
||||
static inline void proc_sys_init(void) { }
|
||||
static inline void sysctl_head_put(struct ctl_table_header *head) { }
|
||||
#endif
|
||||
#ifdef CONFIG_NET
|
||||
extern int proc_net_init(void);
|
||||
#else
|
||||
static inline int proc_net_init(void) { return 0; }
|
||||
#endif
|
||||
struct ctl_table_header;
|
||||
struct mempolicy;
|
||||
|
||||
extern int proc_tid_stat(struct seq_file *m, struct pid_namespace *ns,
|
||||
struct pid *pid, struct task_struct *task);
|
||||
extern int proc_tgid_stat(struct seq_file *m, struct pid_namespace *ns,
|
||||
struct pid *pid, struct task_struct *task);
|
||||
extern int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
|
||||
struct pid *pid, struct task_struct *task);
|
||||
extern int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
|
||||
struct pid *pid, struct task_struct *task);
|
||||
extern loff_t mem_lseek(struct file *file, loff_t offset, int orig);
|
||||
|
||||
extern const struct file_operations proc_tid_children_operations;
|
||||
extern const struct file_operations proc_pid_maps_operations;
|
||||
extern const struct file_operations proc_tid_maps_operations;
|
||||
extern const struct file_operations proc_pid_numa_maps_operations;
|
||||
extern const struct file_operations proc_tid_numa_maps_operations;
|
||||
extern const struct file_operations proc_pid_smaps_operations;
|
||||
extern const struct file_operations proc_tid_smaps_operations;
|
||||
extern const struct file_operations proc_clear_refs_operations;
|
||||
extern const struct file_operations proc_pagemap_operations;
|
||||
extern const struct file_operations proc_net_operations;
|
||||
extern const struct inode_operations proc_net_inode_operations;
|
||||
extern const struct inode_operations proc_pid_link_inode_operations;
|
||||
|
||||
struct proc_maps_private {
|
||||
struct pid *pid;
|
||||
struct task_struct *task;
|
||||
#ifdef CONFIG_MMU
|
||||
struct vm_area_struct *tail_vma;
|
||||
#endif
|
||||
#ifdef CONFIG_NUMA
|
||||
struct mempolicy *task_mempolicy;
|
||||
#endif
|
||||
/*
|
||||
* This is not completely implemented yet. The idea is to
|
||||
* create an in-memory tree (like the actual /proc filesystem
|
||||
* tree) of these proc_dir_entries, so that we can dynamically
|
||||
* add new files to /proc.
|
||||
*
|
||||
* The "next" pointer creates a linked list of one /proc directory,
|
||||
* while parent/subdir create the directory structure (every
|
||||
* /proc file has a parent, but "subdir" is NULL for all
|
||||
* non-directory entries).
|
||||
*/
|
||||
struct proc_dir_entry {
|
||||
unsigned int low_ino;
|
||||
umode_t mode;
|
||||
nlink_t nlink;
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
loff_t size;
|
||||
const struct inode_operations *proc_iops;
|
||||
const struct file_operations *proc_fops;
|
||||
struct proc_dir_entry *next, *parent, *subdir;
|
||||
void *data;
|
||||
atomic_t count; /* use count */
|
||||
atomic_t in_use; /* number of callers into module in progress; */
|
||||
/* negative -> it's going away RSN */
|
||||
struct completion *pde_unload_completion;
|
||||
struct list_head pde_openers; /* who did ->open, but not ->release */
|
||||
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
|
||||
u8 namelen;
|
||||
char name[];
|
||||
};
|
||||
|
||||
void proc_init_inodecache(void);
|
||||
union proc_op {
|
||||
int (*proc_get_link)(struct dentry *, struct path *);
|
||||
int (*proc_read)(struct task_struct *task, char *page);
|
||||
int (*proc_show)(struct seq_file *m,
|
||||
struct pid_namespace *ns, struct pid *pid,
|
||||
struct task_struct *task);
|
||||
};
|
||||
|
||||
struct proc_inode {
|
||||
struct pid *pid;
|
||||
int fd;
|
||||
union proc_op op;
|
||||
struct proc_dir_entry *pde;
|
||||
struct ctl_table_header *sysctl;
|
||||
struct ctl_table *sysctl_entry;
|
||||
struct proc_ns ns;
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
/*
|
||||
* General functions
|
||||
*/
|
||||
static inline struct proc_inode *PROC_I(const struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct proc_inode, vfs_inode);
|
||||
}
|
||||
|
||||
static inline struct proc_dir_entry *PDE(const struct inode *inode)
|
||||
{
|
||||
return PROC_I(inode)->pde;
|
||||
}
|
||||
|
||||
static inline void *__PDE_DATA(const struct inode *inode)
|
||||
{
|
||||
return PDE(inode)->data;
|
||||
}
|
||||
|
||||
static inline struct pid *proc_pid(struct inode *inode)
|
||||
{
|
||||
|
|
@ -76,11 +97,6 @@ static inline struct task_struct *get_proc_task(struct inode *inode)
|
|||
return get_pid_task(proc_pid(inode), PIDTYPE_PID);
|
||||
}
|
||||
|
||||
static inline int proc_fd(struct inode *inode)
|
||||
{
|
||||
return PROC_I(inode)->fd;
|
||||
}
|
||||
|
||||
static inline int task_dumpable(struct task_struct *task)
|
||||
{
|
||||
int dumpable = 0;
|
||||
|
|
@ -96,15 +112,6 @@ static inline int task_dumpable(struct task_struct *task)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int pid_delete_dentry(const struct dentry * dentry)
|
||||
{
|
||||
/* Is the task we represent dead?
|
||||
* If so, then don't put the dentry on the lru list,
|
||||
* kill it immediately.
|
||||
*/
|
||||
return !proc_pid(dentry->d_inode)->tasks[PIDTYPE_PID].first;
|
||||
}
|
||||
|
||||
static inline unsigned name_to_int(struct dentry *dentry)
|
||||
{
|
||||
const char *name = dentry->d_name.name;
|
||||
|
|
@ -127,63 +134,165 @@ out:
|
|||
return ~0U;
|
||||
}
|
||||
|
||||
struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *ino,
|
||||
struct dentry *dentry);
|
||||
int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
|
||||
filldir_t filldir);
|
||||
/*
|
||||
* Offset of the first process in the /proc root directory..
|
||||
*/
|
||||
#define FIRST_PROCESS_ENTRY 256
|
||||
|
||||
struct pde_opener {
|
||||
struct inode *inode;
|
||||
struct file *file;
|
||||
int (*release)(struct inode *, struct file *);
|
||||
struct list_head lh;
|
||||
};
|
||||
void pde_users_dec(struct proc_dir_entry *pde);
|
||||
/* Worst case buffer size needed for holding an integer. */
|
||||
#define PROC_NUMBUF 13
|
||||
|
||||
/*
|
||||
* array.c
|
||||
*/
|
||||
extern const struct file_operations proc_tid_children_operations;
|
||||
|
||||
extern int proc_tid_stat(struct seq_file *, struct pid_namespace *,
|
||||
struct pid *, struct task_struct *);
|
||||
extern int proc_tgid_stat(struct seq_file *, struct pid_namespace *,
|
||||
struct pid *, struct task_struct *);
|
||||
extern int proc_pid_status(struct seq_file *, struct pid_namespace *,
|
||||
struct pid *, struct task_struct *);
|
||||
extern int proc_pid_statm(struct seq_file *, struct pid_namespace *,
|
||||
struct pid *, struct task_struct *);
|
||||
|
||||
/*
|
||||
* base.c
|
||||
*/
|
||||
extern const struct dentry_operations pid_dentry_operations;
|
||||
extern int pid_getattr(struct vfsmount *, struct dentry *, struct kstat *);
|
||||
extern int proc_setattr(struct dentry *, struct iattr *);
|
||||
extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *);
|
||||
extern int pid_revalidate(struct dentry *, unsigned int);
|
||||
extern int pid_delete_dentry(const struct dentry *);
|
||||
extern int proc_pid_readdir(struct file *, void *, filldir_t);
|
||||
extern struct dentry *proc_pid_lookup(struct inode *, struct dentry *, unsigned int);
|
||||
extern loff_t mem_lseek(struct file *, loff_t, int);
|
||||
|
||||
/* Lookups */
|
||||
typedef struct dentry *instantiate_t(struct inode *, struct dentry *,
|
||||
struct task_struct *, const void *);
|
||||
extern int proc_fill_cache(struct file *, void *, filldir_t, const char *, int,
|
||||
instantiate_t, struct task_struct *, const void *);
|
||||
|
||||
/*
|
||||
* generic.c
|
||||
*/
|
||||
extern spinlock_t proc_subdir_lock;
|
||||
|
||||
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsigned int);
|
||||
int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir);
|
||||
unsigned long task_vsize(struct mm_struct *);
|
||||
unsigned long task_statm(struct mm_struct *,
|
||||
unsigned long *, unsigned long *, unsigned long *, unsigned long *);
|
||||
void task_mem(struct seq_file *, struct mm_struct *);
|
||||
extern struct dentry *proc_lookup(struct inode *, struct dentry *, unsigned int);
|
||||
extern struct dentry *proc_lookup_de(struct proc_dir_entry *, struct inode *,
|
||||
struct dentry *);
|
||||
extern int proc_readdir(struct file *, void *, filldir_t);
|
||||
extern int proc_readdir_de(struct proc_dir_entry *, struct file *, void *, filldir_t);
|
||||
|
||||
static inline struct proc_dir_entry *pde_get(struct proc_dir_entry *pde)
|
||||
{
|
||||
atomic_inc(&pde->count);
|
||||
return pde;
|
||||
}
|
||||
void pde_put(struct proc_dir_entry *pde);
|
||||
|
||||
int proc_fill_super(struct super_block *);
|
||||
struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
|
||||
int proc_remount(struct super_block *sb, int *flags, char *data);
|
||||
extern void pde_put(struct proc_dir_entry *);
|
||||
|
||||
/*
|
||||
* These are generic /proc routines that use the internal
|
||||
* "struct proc_dir_entry" tree to traverse the filesystem.
|
||||
*
|
||||
* The /proc root directory has extended versions to take care
|
||||
* of the /proc/<pid> subdirectories.
|
||||
* inode.c
|
||||
*/
|
||||
int proc_readdir(struct file *, void *, filldir_t);
|
||||
struct dentry *proc_lookup(struct inode *, struct dentry *, unsigned int);
|
||||
struct pde_opener {
|
||||
struct file *file;
|
||||
struct list_head lh;
|
||||
int closing;
|
||||
struct completion *c;
|
||||
};
|
||||
|
||||
extern const struct inode_operations proc_pid_link_inode_operations;
|
||||
|
||||
extern void proc_init_inodecache(void);
|
||||
extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
|
||||
extern int proc_fill_super(struct super_block *);
|
||||
extern void proc_entry_rundown(struct proc_dir_entry *);
|
||||
|
||||
/* Lookups */
|
||||
typedef struct dentry *instantiate_t(struct inode *, struct dentry *,
|
||||
struct task_struct *, const void *);
|
||||
int proc_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
|
||||
const char *name, int len,
|
||||
instantiate_t instantiate, struct task_struct *task, const void *ptr);
|
||||
int pid_revalidate(struct dentry *dentry, unsigned int flags);
|
||||
struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task);
|
||||
extern const struct dentry_operations pid_dentry_operations;
|
||||
int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
|
||||
int proc_setattr(struct dentry *dentry, struct iattr *attr);
|
||||
/*
|
||||
* proc_devtree.c
|
||||
*/
|
||||
#ifdef CONFIG_PROC_DEVICETREE
|
||||
extern void proc_device_tree_init(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* proc_namespaces.c
|
||||
*/
|
||||
extern const struct inode_operations proc_ns_dir_inode_operations;
|
||||
extern const struct file_operations proc_ns_dir_operations;
|
||||
|
||||
/*
|
||||
* proc_net.c
|
||||
*/
|
||||
extern const struct file_operations proc_net_operations;
|
||||
extern const struct inode_operations proc_net_inode_operations;
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
extern int proc_net_init(void);
|
||||
#else
|
||||
static inline int proc_net_init(void) { return 0; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* proc_self.c
|
||||
*/
|
||||
extern int proc_setup_self(struct super_block *);
|
||||
|
||||
/*
|
||||
* proc_sysctl.c
|
||||
*/
|
||||
#ifdef CONFIG_PROC_SYSCTL
|
||||
extern int proc_sys_init(void);
|
||||
extern void sysctl_head_put(struct ctl_table_header *);
|
||||
#else
|
||||
static inline void proc_sys_init(void) { }
|
||||
static inline void sysctl_head_put(struct ctl_table_header *head) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* proc_tty.c
|
||||
*/
|
||||
#ifdef CONFIG_TTY
|
||||
extern void proc_tty_init(void);
|
||||
#else
|
||||
static inline void proc_tty_init(void) {}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* root.c
|
||||
*/
|
||||
extern struct proc_dir_entry proc_root;
|
||||
|
||||
extern void proc_self_init(void);
|
||||
extern int proc_remount(struct super_block *, int *, char *);
|
||||
|
||||
/*
|
||||
* task_[no]mmu.c
|
||||
*/
|
||||
struct proc_maps_private {
|
||||
struct pid *pid;
|
||||
struct task_struct *task;
|
||||
#ifdef CONFIG_MMU
|
||||
struct vm_area_struct *tail_vma;
|
||||
#endif
|
||||
#ifdef CONFIG_NUMA
|
||||
struct mempolicy *task_mempolicy;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern const struct file_operations proc_pid_maps_operations;
|
||||
extern const struct file_operations proc_tid_maps_operations;
|
||||
extern const struct file_operations proc_pid_numa_maps_operations;
|
||||
extern const struct file_operations proc_tid_numa_maps_operations;
|
||||
extern const struct file_operations proc_pid_smaps_operations;
|
||||
extern const struct file_operations proc_tid_smaps_operations;
|
||||
extern const struct file_operations proc_clear_refs_operations;
|
||||
extern const struct file_operations proc_pagemap_operations;
|
||||
|
||||
extern unsigned long task_vsize(struct mm_struct *);
|
||||
extern unsigned long task_statm(struct mm_struct *,
|
||||
unsigned long *, unsigned long *,
|
||||
unsigned long *, unsigned long *);
|
||||
extern void task_mem(struct seq_file *, struct mm_struct *);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/kcore.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/elf.h>
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/memory.h>
|
||||
#include <asm/sections.h>
|
||||
#include "internal.h"
|
||||
|
||||
#define CORE_STR "CORE"
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ static int ns_delete_dentry(const struct dentry *dentry)
|
|||
static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
|
||||
const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns.ns_ops;
|
||||
|
||||
return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]",
|
||||
ns_ops->name, inode->i_ino);
|
||||
|
|
@ -95,8 +95,8 @@ static struct dentry *proc_ns_get_dentry(struct super_block *sb,
|
|||
inode->i_op = &ns_inode_operations;
|
||||
inode->i_mode = S_IFREG | S_IRUGO;
|
||||
inode->i_fop = &ns_file_operations;
|
||||
ei->ns_ops = ns_ops;
|
||||
ei->ns = ns;
|
||||
ei->ns.ns_ops = ns_ops;
|
||||
ei->ns.ns = ns;
|
||||
unlock_new_inode(inode);
|
||||
} else {
|
||||
ns_ops->put(ns);
|
||||
|
|
@ -128,7 +128,7 @@ static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)
|
|||
if (!ptrace_may_access(task, PTRACE_MODE_READ))
|
||||
goto out_put_task;
|
||||
|
||||
ns_path.dentry = proc_ns_get_dentry(sb, task, ei->ns_ops);
|
||||
ns_path.dentry = proc_ns_get_dentry(sb, task, ei->ns.ns_ops);
|
||||
if (IS_ERR(ns_path.dentry)) {
|
||||
error = ERR_CAST(ns_path.dentry);
|
||||
goto out_put_task;
|
||||
|
|
@ -148,7 +148,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
|
|||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct proc_inode *ei = PROC_I(inode);
|
||||
const struct proc_ns_operations *ns_ops = ei->ns_ops;
|
||||
const struct proc_ns_operations *ns_ops = ei->ns.ns_ops;
|
||||
struct task_struct *task;
|
||||
void *ns;
|
||||
char name[50];
|
||||
|
|
@ -202,7 +202,7 @@ static struct dentry *proc_ns_instantiate(struct inode *dir,
|
|||
ei = PROC_I(inode);
|
||||
inode->i_mode = S_IFLNK|S_IRWXUGO;
|
||||
inode->i_op = &proc_ns_link_inode_operations;
|
||||
ei->ns_ops = ns_ops;
|
||||
ei->ns.ns_ops = ns_ops;
|
||||
|
||||
d_set_d_op(dentry, &pid_dentry_operations);
|
||||
d_add(dentry, inode);
|
||||
|
|
@ -337,6 +337,11 @@ out_invalid:
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
struct proc_ns *get_proc_ns(struct inode *inode)
|
||||
{
|
||||
return &PROC_I(inode)->ns;
|
||||
}
|
||||
|
||||
bool proc_ns_inode(struct inode *inode)
|
||||
{
|
||||
return inode->i_fop == &ns_file_operations;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ static int property_proc_show(struct seq_file *m, void *v)
|
|||
|
||||
static int property_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, property_proc_show, PDE(inode)->data);
|
||||
return single_open(file, property_proc_show, __PDE_DATA(inode));
|
||||
}
|
||||
|
||||
static const struct file_operations property_proc_fops = {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@
|
|||
|
||||
#include "internal.h"
|
||||
|
||||
static inline struct net *PDE_NET(struct proc_dir_entry *pde)
|
||||
{
|
||||
return pde->parent->data;
|
||||
}
|
||||
|
||||
static struct net *get_proc_net(const struct inode *inode)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -141,6 +141,8 @@ static void proc_kill_sb(struct super_block *sb)
|
|||
struct pid_namespace *ns;
|
||||
|
||||
ns = (struct pid_namespace *)sb->s_fs_info;
|
||||
if (ns->proc_self)
|
||||
dput(ns->proc_self);
|
||||
kill_anon_super(sb);
|
||||
put_pid_ns(ns);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include <linux/proc_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* /proc/self:
|
||||
|
|
@ -48,12 +50,43 @@ static const struct inode_operations proc_self_inode_operations = {
|
|||
.put_link = proc_self_put_link,
|
||||
};
|
||||
|
||||
static unsigned self_inum;
|
||||
|
||||
int proc_setup_self(struct super_block *s)
|
||||
{
|
||||
struct inode *root_inode = s->s_root->d_inode;
|
||||
struct pid_namespace *ns = s->s_fs_info;
|
||||
struct dentry *self;
|
||||
|
||||
mutex_lock(&root_inode->i_mutex);
|
||||
self = d_alloc_name(s->s_root, "self");
|
||||
if (self) {
|
||||
struct inode *inode = new_inode_pseudo(s);
|
||||
if (inode) {
|
||||
inode->i_ino = self_inum;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_mode = S_IFLNK | S_IRWXUGO;
|
||||
inode->i_uid = GLOBAL_ROOT_UID;
|
||||
inode->i_gid = GLOBAL_ROOT_GID;
|
||||
inode->i_op = &proc_self_inode_operations;
|
||||
d_add(self, inode);
|
||||
} else {
|
||||
dput(self);
|
||||
self = ERR_PTR(-ENOMEM);
|
||||
}
|
||||
} else {
|
||||
self = ERR_PTR(-ENOMEM);
|
||||
}
|
||||
mutex_unlock(&root_inode->i_mutex);
|
||||
if (IS_ERR(self)) {
|
||||
pr_err("proc_fill_super: can't allocate /proc/self\n");
|
||||
return PTR_ERR(self);
|
||||
}
|
||||
ns->proc_self = self;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init proc_self_init(void)
|
||||
{
|
||||
struct proc_dir_entry *proc_self_symlink;
|
||||
mode_t mode;
|
||||
|
||||
mode = S_IFLNK | S_IRWXUGO;
|
||||
proc_self_symlink = proc_create("self", mode, NULL, NULL );
|
||||
proc_self_symlink->proc_iops = &proc_self_inode_operations;
|
||||
proc_alloc_inum(&self_inum);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/kcore.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/elfcore.h>
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
#include "internal.h"
|
||||
|
||||
/* List representing chunks of contiguous memory areas and their offsets in
|
||||
* vmcore file.
|
||||
|
|
@ -698,7 +699,7 @@ void vmcore_cleanup(void)
|
|||
struct list_head *pos, *next;
|
||||
|
||||
if (proc_vmcore) {
|
||||
remove_proc_entry(proc_vmcore->name, proc_vmcore->parent);
|
||||
proc_remove(proc_vmcore);
|
||||
proc_vmcore = NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue