1 /* -*- linux-c -*- --------------------------------------------------------- *
2  *
3  * linux/fs/devpts/root.c
4  *
5  *  Copyright 1998 H. Peter Anvin -- All Rights Reserved
6  *
7  * This file is part of the Linux kernel and is made available under
8  * the terms of the GNU General Public License, version 2, or at your
9  * option, any later version, incorporated herein by reference.
10  *
11  * ------------------------------------------------------------------------- */
12 
13 #include <linux/errno.h>
14 #include <linux/stat.h>
15 #include <linux/param.h>
16 #include <linux/string.h>
17 #include "devpts_i.h"
18 
19 static int devpts_root_readdir(struct file *,void *,filldir_t);
20 static struct dentry *devpts_root_lookup(struct inode *,struct dentry *);
21 static int devpts_revalidate(struct dentry *, int);
22 
23 struct file_operations devpts_root_operations = {
24 	read:		generic_read_dir,
25 	readdir:	devpts_root_readdir,
26 };
27 
28 struct inode_operations devpts_root_inode_operations = {
29 	lookup:		devpts_root_lookup,
30 };
31 
32 static struct dentry_operations devpts_dentry_operations = {
33 	d_revalidate:	devpts_revalidate,
34 };
35 
36 /*
37  * The normal naming convention is simply /dev/pts/<number>; this conforms
38  * to the System V naming convention
39  */
40 
41 #define genptsname(buf,num) sprintf(buf, "%d", num)
42 
devpts_root_readdir(struct file * filp,void * dirent,filldir_t filldir)43 static int devpts_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
44 {
45 	struct inode * inode = filp->f_dentry->d_inode;
46 	struct devpts_sb_info * sbi = SBI(filp->f_dentry->d_inode->i_sb);
47 	off_t nr;
48 	char numbuf[16];
49 
50 	nr = filp->f_pos;
51 
52 	switch(nr)
53 	{
54 	case 0:
55 		if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0)
56 			return 0;
57 		filp->f_pos = ++nr;
58 		/* fall through */
59 	case 1:
60 		if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0)
61 			return 0;
62 		filp->f_pos = ++nr;
63 		/* fall through */
64 	default:
65 		while ( nr - 2 < sbi->max_ptys ) {
66 			int ptynr = nr - 2;
67 			if ( sbi->inodes[ptynr] ) {
68 				genptsname(numbuf, ptynr);
69 				if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr, DT_CHR) < 0 )
70 					return 0;
71 			}
72 			filp->f_pos = ++nr;
73 		}
74 		break;
75 	}
76 
77 	return 0;
78 }
79 
80 /*
81  * Revalidate is called on every cache lookup.  We use it to check that
82  * the pty really does still exist.  Never revalidate negative dentries;
83  * for simplicity (fix later?)
84  */
devpts_revalidate(struct dentry * dentry,int flags)85 static int devpts_revalidate(struct dentry * dentry, int flags)
86 {
87 	struct devpts_sb_info *sbi;
88 
89 	if ( !dentry->d_inode )
90 		return 0;
91 
92 	sbi = SBI(dentry->d_inode->i_sb);
93 
94 	return ( sbi->inodes[dentry->d_inode->i_ino - 2] == dentry->d_inode );
95 }
96 
devpts_root_lookup(struct inode * dir,struct dentry * dentry)97 static struct dentry *devpts_root_lookup(struct inode * dir, struct dentry * dentry)
98 {
99 	struct devpts_sb_info *sbi = SBI(dir->i_sb);
100 	unsigned int entry;
101 	int i;
102 	const char *p;
103 
104 	dentry->d_op    = &devpts_dentry_operations;
105 
106 	if ( dentry->d_name.len == 1 && dentry->d_name.name[0] == '0' ) {
107 		entry = 0;
108 	} else if ( dentry->d_name.len < 1 ) {
109 		return NULL;
110 	} else {
111 		p = dentry->d_name.name;
112 		if ( *p < '1' || *p > '9' )
113 			return NULL;
114 		entry = *p++ - '0';
115 
116 		for ( i = dentry->d_name.len-1 ; i ; i-- ) {
117 			unsigned int nentry = *p++ - '0';
118 			if ( nentry > 9 )
119 				return NULL;
120 			if ( entry >= ~0U/10 )
121 				return NULL;
122 			entry = nentry + entry * 10;
123 		}
124 	}
125 
126 	if ( entry >= sbi->max_ptys )
127 		return NULL;
128 
129 	if ( sbi->inodes[entry] )
130 		atomic_inc(&sbi->inodes[entry]->i_count);
131 
132 	d_add(dentry, sbi->inodes[entry]);
133 
134 	return NULL;
135 }
136