patch-1.3.19 linux/fs/proc/root.c

Next file: linux/fs/proc/scsi.c
Previous file: linux/fs/proc/net.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.18/linux/fs/proc/root.c linux/fs/proc/root.c
@@ -14,14 +14,19 @@
 #include <linux/stat.h>
 #include <linux/config.h>
 
-static int proc_readroot(struct inode *, struct file *, void *, filldir_t);
-static int proc_lookuproot(struct inode *,const char *,int,struct inode **);
+/*
+ * Offset of the first process in the /proc root directory..
+ */
+#define FIRST_PROCESS_ENTRY 256
+
+static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t);
+static int proc_root_lookup(struct inode *,const char *,int,struct inode **);
 
 static struct file_operations proc_root_operations = {
 	NULL,			/* lseek - default */
 	NULL,			/* read - bad */
 	NULL,			/* write - bad */
-	proc_readroot,		/* readdir */
+	proc_root_readdir,	/* readdir */
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	NULL,			/* mmap */
@@ -33,10 +38,10 @@
 /*
  * proc directories can do almost nothing..
  */
-struct inode_operations proc_root_inode_operations = {
+static struct inode_operations proc_root_inode_operations = {
 	&proc_root_operations,	/* default base directory file-ops */
 	NULL,			/* create */
-	proc_lookuproot,	/* lookup */
+	proc_root_lookup,	/* lookup */
 	NULL,			/* link */
 	NULL,			/* unlink */
 	NULL,			/* symlink */
@@ -51,125 +56,368 @@
 	NULL			/* permission */
 };
 
-static struct proc_dir_entry root_dir[] = {
-	{ PROC_ROOT_INO,	1, "." },
-	{ PROC_ROOT_INO,	2, ".." },
-	{ PROC_LOADAVG,		7, "loadavg" },
-	{ PROC_UPTIME,		6, "uptime" },
-	{ PROC_MEMINFO,		7, "meminfo" },
-	{ PROC_KMSG,		4, "kmsg" },
-	{ PROC_VERSION,		7, "version" },
+/*
+ * This is the root "inode" in the /proc tree..
+ */
+struct proc_dir_entry proc_root = {
+	PROC_ROOT_INO, 5, "/proc",
+	S_IFDIR | S_IRUGO | S_IXUGO, 3, 0, 0,
+	0, &proc_root_inode_operations,
+	NULL, NULL,
+	NULL,
+	&proc_root, NULL
+};
+
+int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
+{
+	dp->next = dir->subdir;
+	dp->parent = dir;
+	dir->subdir = dp;
+	if (S_ISDIR(dp->mode))
+		dir->nlink++;
+	return 0;
+}
+
+int proc_unregister(struct proc_dir_entry * dir, int ino)
+{
+	struct proc_dir_entry **p = &dir->subdir, *dp;
+
+	while ((dp = *p) != NULL) {
+		if (dp->low_ino == ino) {
+			*p = dp->next;
+			dp->next = NULL;
+			if (S_ISDIR(dp->mode))
+				dir->nlink--;
+			return 0;
+		}
+		p = &dp->next;
+	}
+	return -EINVAL;
+}	
+
+/*
+ * /proc/self:
+ */
+static int proc_self_followlink(struct inode * dir, struct inode * inode,
+			int flag, int mode, struct inode ** res_inode)
+{
+	iput(dir);
+	*res_inode = proc_get_inode(inode->i_sb, (current->pid << 16) + PROC_PID_INO, &proc_pid);
+	iput(inode);
+	if (!*res_inode)
+		return -ENOENT;
+	return 0;
+}
+
+static int proc_self_readlink(struct inode * inode, char * buffer, int buflen)
+{
+	int len;
+	char tmp[30];
+
+	iput(inode);
+	len = 1 + sprintf(tmp, "%d", current->pid);
+	if (buflen < len)
+		len = buflen;
+	memcpy_tofs(buffer, tmp, len);
+	return len;
+}
+
+static struct inode_operations proc_self_inode_operations = {
+	NULL,			/* no file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	proc_self_readlink,	/* readlink */
+	proc_self_followlink,	/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+void proc_root_init(void)
+{
+	static int done = 0;
+
+	if (done)
+		return;
+	done = 1;
+	proc_base_init();
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_LOADAVG, 7, "loadavg",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_UPTIME, 6, "uptime",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_MEMINFO, 7, "meminfo",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_KMSG, 4, "kmsg",
+		S_IFREG | S_IRUSR, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_VERSION, 7, "version",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
 #ifdef CONFIG_PCI
-	{ PROC_PCI,             3, "pci" },
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_PCI, 3, "pci",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
 #endif
-	{ PROC_CPUINFO,		7, "cpuinfo" },
-	{ PROC_SELF,		4, "self" },	/* will change inode # */
-	{ PROC_NET,		3, "net" },
-   	{ PROC_SCSI,	        4, "scsi" },
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_CPUINFO, 7, "cpuinfo",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_SELF, 4, "self",
+		S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 1, 0, 0,
+		64, &proc_self_inode_operations,
+	});
+	proc_register(&proc_root, &proc_net);
+	proc_register(&proc_root, &proc_scsi);
 #ifdef CONFIG_DEBUG_MALLOC
-	{ PROC_MALLOC,		6, "malloc" },
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_MALLOC, 6, "malloc",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
 #endif
-	{ PROC_KCORE,		5, "kcore" },
-   	{ PROC_MODULES,		7, "modules" },
-   	{ PROC_STAT,		4, "stat" },
-   	{ PROC_DEVICES,		7, "devices" },
-	{ PROC_INTERRUPTS,	10,"interrupts" },
-   	{ PROC_FILESYSTEMS,	11,"filesystems" },
-   	{ PROC_KSYMS,		5, "ksyms" },
-   	{ PROC_DMA,		3, "dma" },
-	{ PROC_IOPORTS,		7, "ioports" },
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_KCORE, 5, "kcore",
+		S_IFREG | S_IRUSR, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_MODULES, 7, "modules",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_STAT, 4, "stat",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_DEVICES, 7, "devices",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_INTERRUPTS, 10,"interrupts",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_FILESYSTEMS, 11,"filesystems",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_KSYMS, 5, "ksyms",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_DMA, 3, "dma",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_IOPORTS, 7, "ioports",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
 #ifdef CONFIG_PROFILE
-	{ PROC_PROFILE,		7, "profile" },
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_PROFILE, 7, "profile",
+		S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
+	});
 #endif
-};
+}
+
 
-#define NR_ROOT_DIRENTRY ((sizeof (root_dir))/(sizeof (root_dir[0])))
+int proc_match(int len,const char * name,struct proc_dir_entry * de)
+{
+	if (!de || !de->low_ino)
+		return 0;
+	/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+	if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
+		return 1;
+	if (de->namelen != len)
+		return 0;
+	return !memcmp(name, de->name, len);
+}
 
-static int proc_lookuproot(struct inode * dir,const char * name, int len,
+int proc_lookup(struct inode * dir,const char * name, int len,
 	struct inode ** result)
 {
-	struct proc_dir_entry * de = NULL;
-	unsigned int pid, c;
-	int i, ino;
+	struct proc_dir_entry * de;
+	int ino;
 
 	*result = NULL;
-	if (!dir)
-		return -ENOENT;
-	if (!S_ISDIR(dir->i_mode)) {
+	if (!dir || !S_ISDIR(dir->i_mode)) {
 		iput(dir);
-		return -ENOENT;
-	}
-	for (i = 0; i < NR_ROOT_DIRENTRY; i++) {
-		if (!proc_match(len,name,root_dir+i))
-			continue;
-		de = root_dir + i;
-		break;
+		return -ENOTDIR;
 	}
-	if (de) {
-		ino = de->low_ino;
-		if (ino == PROC_ROOT_INO) {
-			*result = dir;
+
+	de = (struct proc_dir_entry *) dir->u.generic_ip;
+	if (!de)
+		return -EINVAL;
+
+	/* Special case "." and "..": they aren't on the directory list */
+	*result = dir;
+	if (!len)
+		return 0;
+	if (name[0] == '.') {
+		if (len == 1)
+			return 0;
+		if (name[1] == '.' && len == 2) {
+			struct inode * inode;
+			inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent);
+			iput(dir);
+			if (!inode)
+				return -EINVAL;
+			*result = inode;
 			return 0;
 		}
-		if (ino == PROC_SELF) /* self modifying inode ... */
-			ino = (current->pid << 16) + 2;
-	} else {
-		pid = 0;
-		while (len-- > 0) {
-			c = *name - '0';
-			name++;
-			if (c > 9) {
-				pid = 0;
-				break;
-			}
-			pid *= 10;
-			pid += c;
-			if (pid & 0xffff0000) {
-				pid = 0;
-				break;
-			}
+	}
+
+	*result = NULL;
+	for (de = de->subdir; de ; de = de->next) {
+		if (proc_match(len, name, de))
+			break;
+	}
+	if (!de)
+		return -ENOENT;
+
+	ino = de->low_ino | (dir->i_ino & ~(0xffff));
+
+	if (!(*result = proc_get_inode(dir->i_sb, ino, de))) {
+		iput(dir);
+		return -EINVAL;
+	}
+	iput(dir);
+	return 0;
+}
+
+static int proc_root_lookup(struct inode * dir,const char * name, int len,
+	struct inode ** result)
+{
+	unsigned int pid, c;
+	int i, ino;
+
+	int retval = proc_lookup(dir, name, len, result);
+	if (retval != -ENOENT)
+		return retval;
+	
+	pid = 0;
+	while (len-- > 0) {
+		c = *name - '0';
+		name++;
+		if (c > 9) {
+			pid = 0;
+			break;
 		}
-		for (i = 0 ; i < NR_TASKS ; i++)
-			if (task[i] && task[i]->pid == pid)
-				break;
-		if (!pid || i >= NR_TASKS) {
-			iput(dir);
-			return -ENOENT;
+		pid *= 10;
+		pid += c;
+		if (pid & 0xffff0000) {
+			pid = 0;
+			break;
 		}
-		ino = (pid << 16) + 2;
 	}
-	if (!(*result = iget(dir->i_sb,ino))) {
+	for (i = 0 ; i < NR_TASKS ; i++)
+		if (task[i] && task[i]->pid == pid)
+			break;
+	if (!pid || i >= NR_TASKS) {
 		iput(dir);
 		return -ENOENT;
 	}
+	ino = (pid << 16) + PROC_PID_INO;
+	if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) {
+		iput(dir);
+		return -EINVAL;
+	}
 	iput(dir);
-	(*result)->u.generic_ip = de;
 	return 0;
 }
 
+/*
+ * This returns non-zero if at EOF, so that the /proc
+ * root directory can use this and check if it should
+ * continue with the <pid> entries..
+ *
+ * Note that the VFS-layer doesn't care about the return
+ * value of the readdir() call, as long as it's non-negative
+ * for success..
+ */
+int proc_readdir(struct inode * inode, struct file * filp,
+	void * dirent, filldir_t filldir)
+{
+	struct proc_dir_entry * de;
+	unsigned int ino;
+	int i;
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		return -ENOTDIR;
+	ino = inode->i_ino;
+	de = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!de)
+		return -EINVAL;
+	i = filp->f_pos;
+	switch (i) {
+		case 0:
+			if (filldir(dirent, ".", 1, i, ino) < 0)
+				return 0;
+			i++;
+			filp->f_pos++;
+			/* fall through */
+		case 1:
+			if (filldir(dirent, "..", 2, i, de->parent->low_ino) < 0)
+				return 0;
+			i++;
+			filp->f_pos++;
+			/* fall through */
+		default:
+			ino &= ~0xffff;
+			de = de->subdir;
+			i -= 2;
+			for (;;) {
+				if (!de)
+					return 1;
+				if (!i)
+					break;
+				de = de->next;
+				i--;
+			}
+
+			do {
+				if (filldir(dirent, de->name, de->namelen, filp->f_pos, ino | de->low_ino) < 0)
+					return 0;
+				filp->f_pos++;
+				de = de->next;
+			} while (de);
+	}
+	return 1;
+}
+
 #define NUMBUF 10
 
-static int proc_readroot(struct inode * inode, struct file * filp,
+static int proc_root_readdir(struct inode * inode, struct file * filp,
 	void * dirent, filldir_t filldir)
 {
 	char buf[NUMBUF];
 	unsigned int nr,pid;
 	unsigned long i,j;
 
-	if (!inode || !S_ISDIR(inode->i_mode))
-		return -EBADF;
-
 	nr = filp->f_pos;
-	while (nr < NR_ROOT_DIRENTRY) {
-		struct proc_dir_entry * de = root_dir + nr;
-
-		if (filldir(dirent, de->name, de->namelen, nr, de->low_ino) < 0)
-			return 0;
-		filp->f_pos++;
-		nr++;
+	if (nr < FIRST_PROCESS_ENTRY) {
+		int error = proc_readdir(inode, filp, dirent, filldir);
+		if (error <= 0)
+			return error;
+		filp->f_pos = nr = FIRST_PROCESS_ENTRY;
 	}
 
-	for (nr -= NR_ROOT_DIRENTRY; nr < NR_TASKS; nr++, filp->f_pos++) {
+	for (nr -= FIRST_PROCESS_ENTRY; nr < NR_TASKS; nr++, filp->f_pos++) {
 		struct task_struct * p = task[nr];
 
 		if (!p || !(pid = p->pid))
@@ -183,7 +431,7 @@
 			i /= 10;
 		} while (i);
 
-		if (filldir(dirent, buf+j, NUMBUF-j, nr+NR_ROOT_DIRENTRY, (pid << 16)+2) < 0)
+		if (filldir(dirent, buf+j, NUMBUF-j, filp->f_pos, (pid << 16) + PROC_PID_INO) < 0)
 			break;
 	}
 	return 0;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this