summaryrefslogtreecommitdiffstats
path: root/fs/proc/generic.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
commit230e5ab6a084ed50470f101934782dbf54b0d06b (patch)
tree5dd821c8d33f450470588e7a543f74bf74306e9e /fs/proc/generic.c
parentc9b1c8a64c6444d189856f1e26bdcb8b4cd0113a (diff)
Merge with Linux 2.1.67.
Diffstat (limited to 'fs/proc/generic.c')
-rw-r--r--fs/proc/generic.c139
1 files changed, 79 insertions, 60 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 83139f492..89da7544b 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -16,11 +16,13 @@
#include <linux/stat.h>
#include <asm/bitops.h>
-static long proc_file_read(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes);
-static long proc_file_write(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count);
-static long long proc_file_lseek(struct file * file, long long offset, int orig);
+extern struct inode_operations proc_dyna_dir_inode_operations;
+
+static ssize_t proc_file_read(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos);
+static ssize_t proc_file_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos);
+static long long proc_file_lseek(struct file *, long long, int);
int proc_match(int len, const char *name,struct proc_dir_entry * de)
{
@@ -44,17 +46,14 @@ static struct file_operations proc_file_operations = {
NULL /* can't fsync */
};
-/*
- * proc files can do almost nothing..
- */
struct inode_operations proc_file_inode_operations = {
- &proc_file_operations, /* default proc file-ops */
- NULL, /* create */
- NULL, /* lookup */
- NULL, /* link */
- NULL, /* unlink */
- NULL, /* symlink */
- NULL, /* mkdir */
+ &proc_file_operations, /* default proc file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
@@ -98,18 +97,17 @@ struct inode_operations proc_net_inode_operations = {
/* 4K page size but our output routines use some slack for overruns */
#define PROC_BLOCK_SIZE (3*1024)
-static long proc_file_read(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes)
+static ssize_t
+proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
{
+ struct inode * inode = file->f_dentry->d_inode;
char *page;
- int retval=0;
+ ssize_t retval=0;
int eof=0;
- int n, count;
+ ssize_t n, count;
char *start;
struct proc_dir_entry * dp;
- if (nbytes < 0)
- return -EINVAL;
dp = (struct proc_dir_entry *) inode->u.generic_ip;
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
@@ -127,12 +125,12 @@ static long proc_file_read(struct inode * inode, struct file * file,
* XXX What gives with the file->f_flags & O_ACCMODE
* test? Seems stupid to me....
*/
- n = dp->get_info(page, &start, file->f_pos, count,
+ n = dp->get_info(page, &start, *ppos, count,
(file->f_flags & O_ACCMODE) == O_RDWR);
if (n < count)
eof = 1;
} else if (dp->read_proc) {
- n = dp->read_proc(page, &start, file->f_pos,
+ n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
} else
break;
@@ -141,8 +139,8 @@ static long proc_file_read(struct inode * inode, struct file * file,
/*
* For proc files that are less than 4k
*/
- start = page + file->f_pos;
- n -= file->f_pos;
+ start = page + *ppos;
+ n -= *ppos;
if (n <= 0)
break;
if (n > count)
@@ -163,7 +161,7 @@ static long proc_file_read(struct inode * inode, struct file * file,
break;
}
- file->f_pos += n; /* Move down the file */
+ *ppos += n; /* Move down the file */
nbytes -= n;
buf += n;
retval += n;
@@ -172,24 +170,25 @@ static long proc_file_read(struct inode * inode, struct file * file,
return retval;
}
-static long
-proc_file_write(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t
+proc_file_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = file->f_dentry->d_inode;
struct proc_dir_entry * dp;
- if (count < 0)
- return -EINVAL;
dp = (struct proc_dir_entry *) inode->u.generic_ip;
if (!dp->write_proc)
return -EIO;
+ /* FIXME: does this routine need ppos? probably... */
return dp->write_proc(file, buffer, count, dp->data);
}
-static long long proc_file_lseek(struct file * file, long long offset, int orig)
+static long long
+proc_file_lseek(struct file * file, long long offset, int orig)
{
switch (orig) {
case 0:
@@ -240,57 +239,77 @@ static int xlate_proc_name(const char *name,
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
- struct proc_dir_entry *ent;
- const char *fn;
-
- if (parent)
- fn = name;
- else {
- if (xlate_proc_name(name, &parent, &fn))
- return NULL;
- }
+ struct proc_dir_entry *ent = NULL;
+ const char *fn = name;
+ int len;
+
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
+ len = strlen(fn);
- ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+ ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
if (!ent)
- return NULL;
+ goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));
+ memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);
+ ent->name = ((char *) ent) + sizeof(*ent);
+ ent->namelen = len;
- if (mode == S_IFDIR)
+ if (mode == S_IFDIR) {
mode |= S_IRUGO | S_IXUGO;
- else if (mode == 0)
- mode = S_IFREG | S_IRUGO;
-
- ent->name = fn;
- ent->namelen = strlen(fn);
- ent->mode = mode;
- if (S_ISDIR(mode))
+ ent->ops = &proc_dyna_dir_inode_operations;
ent->nlink = 2;
- else
+ }
+ else if (mode == 0) {
+ mode = S_IFREG | S_IRUGO;
ent->nlink = 1;
+ }
+ ent->mode = mode;
proc_register(parent, ent);
+out:
return ent;
}
+extern void free_proc_entry(struct proc_dir_entry *);
+void free_proc_entry(struct proc_dir_entry *de)
+{
+ kfree(de);
+}
+
+/*
+ * Remove a /proc entry and free it if it's not currently in use.
+ * If it is in use, we set the 'deleted' flag.
+ */
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
struct proc_dir_entry *de;
- const char *fn;
+ const char *fn = name;
int len;
- if (parent)
- fn = name;
- else
- if (xlate_proc_name(name, &parent, &fn))
- return;
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
len = strlen(fn);
for (de = parent->subdir; de ; de = de->next) {
if (proc_match(len, fn, de))
break;
}
- if (de)
+
+ if (de) {
+printk("remove_proc_entry: parent nlink=%d, file nlink=%d\n",
+parent->nlink, de->nlink);
proc_unregister(parent, de->low_ino);
- kfree(de);
+ de->nlink = 0;
+ de->deleted = 1;
+ if (!de->count)
+ free_proc_entry(de);
+ else {
+ printk("remove_proc_entry: %s/%s busy, count=%d\n",
+ parent->name, de->name, de->count);
+ }
+ }
+out:
+ return;
}