Skip to content

Commit 564def7

Browse files
committed
proc: Add a way to make network proc files writable
Provide two extra functions, proc_create_net_data_write() and proc_create_net_single_write() that act like their non-write versions but also set a write method in the proc_dir_entry struct. An internal simple write function is provided that will copy its buffer and hand it to the pde->write() method if available (or give an error if not). The buffer may be modified by the write method. Signed-off-by: David Howells <[email protected]>
1 parent 5d9de25 commit 564def7

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

fs/proc/generic.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,3 +741,27 @@ void *PDE_DATA(const struct inode *inode)
741741
return __PDE_DATA(inode);
742742
}
743743
EXPORT_SYMBOL(PDE_DATA);
744+
745+
/*
746+
* Pull a user buffer into memory and pass it to the file's write handler if
747+
* one is supplied. The ->write() method is permitted to modify the
748+
* kernel-side buffer.
749+
*/
750+
ssize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size,
751+
loff_t *_pos)
752+
{
753+
struct proc_dir_entry *pde = PDE(file_inode(f));
754+
char *buf;
755+
int ret;
756+
757+
if (!pde->write)
758+
return -EACCES;
759+
if (size == 0 || size > PAGE_SIZE - 1)
760+
return -EINVAL;
761+
buf = memdup_user_nul(ubuf, size);
762+
if (IS_ERR(buf))
763+
return PTR_ERR(buf);
764+
ret = pde->write(f, buf, size);
765+
kfree(buf);
766+
return ret == 0 ? size : ret;
767+
}

fs/proc/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct proc_dir_entry {
4848
const struct seq_operations *seq_ops;
4949
int (*single_show)(struct seq_file *, void *);
5050
};
51+
proc_write_t write;
5152
void *data;
5253
unsigned int state_size;
5354
unsigned int low_ino;
@@ -187,6 +188,7 @@ static inline bool is_empty_pde(const struct proc_dir_entry *pde)
187188
{
188189
return S_ISDIR(pde->mode) && !pde->proc_iops;
189190
}
191+
extern ssize_t proc_simple_write(struct file *, const char __user *, size_t, loff_t *);
190192

191193
/*
192194
* inode.c

fs/proc/proc_net.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ static int seq_open_net(struct inode *inode, struct file *file)
4646

4747
WARN_ON_ONCE(state_size < sizeof(*p));
4848

49+
if (file->f_mode & FMODE_WRITE && !PDE(inode)->write)
50+
return -EACCES;
51+
4952
net = get_proc_net(inode);
5053
if (!net)
5154
return -ENXIO;
@@ -73,6 +76,7 @@ static int seq_release_net(struct inode *ino, struct file *f)
7376
static const struct file_operations proc_net_seq_fops = {
7477
.open = seq_open_net,
7578
.read = seq_read,
79+
.write = proc_simple_write,
7680
.llseek = seq_lseek,
7781
.release = seq_release_net,
7882
};
@@ -93,6 +97,50 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
9397
}
9498
EXPORT_SYMBOL_GPL(proc_create_net_data);
9599

100+
/**
101+
* proc_create_net_data_write - Create a writable net_ns-specific proc file
102+
* @name: The name of the file.
103+
* @mode: The file's access mode.
104+
* @parent: The parent directory in which to create.
105+
* @ops: The seq_file ops with which to read the file.
106+
* @write: The write method which which to 'modify' the file.
107+
* @data: Data for retrieval by PDE_DATA().
108+
*
109+
* Create a network namespaced proc file in the @parent directory with the
110+
* specified @name and @mode that allows reading of a file that displays a
111+
* series of elements and also provides for the file accepting writes that have
112+
* some arbitrary effect.
113+
*
114+
* The functions in the @ops table are used to iterate over items to be
115+
* presented and extract the readable content using the seq_file interface.
116+
*
117+
* The @write function is called with the data copied into a kernel space
118+
* scratch buffer and has a NUL appended for convenience. The buffer may be
119+
* modified by the @write function. @write should return 0 on success.
120+
*
121+
* The @data value is accessible from the @show and @write functions by calling
122+
* PDE_DATA() on the file inode. The network namespace must be accessed by
123+
* calling seq_file_net() on the seq_file struct.
124+
*/
125+
struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
126+
struct proc_dir_entry *parent,
127+
const struct seq_operations *ops,
128+
proc_write_t write,
129+
unsigned int state_size, void *data)
130+
{
131+
struct proc_dir_entry *p;
132+
133+
p = proc_create_reg(name, mode, &parent, data);
134+
if (!p)
135+
return NULL;
136+
p->proc_fops = &proc_net_seq_fops;
137+
p->seq_ops = ops;
138+
p->state_size = state_size;
139+
p->write = write;
140+
return proc_register(parent, p);
141+
}
142+
EXPORT_SYMBOL_GPL(proc_create_net_data_write);
143+
96144
static int single_open_net(struct inode *inode, struct file *file)
97145
{
98146
struct proc_dir_entry *de = PDE(inode);
@@ -119,6 +167,7 @@ static int single_release_net(struct inode *ino, struct file *f)
119167
static const struct file_operations proc_net_single_fops = {
120168
.open = single_open_net,
121169
.read = seq_read,
170+
.write = proc_simple_write,
122171
.llseek = seq_lseek,
123172
.release = single_release_net,
124173
};
@@ -138,6 +187,49 @@ struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
138187
}
139188
EXPORT_SYMBOL_GPL(proc_create_net_single);
140189

190+
/**
191+
* proc_create_net_single_write - Create a writable net_ns-specific proc file
192+
* @name: The name of the file.
193+
* @mode: The file's access mode.
194+
* @parent: The parent directory in which to create.
195+
* @show: The seqfile show method with which to read the file.
196+
* @write: The write method which which to 'modify' the file.
197+
* @data: Data for retrieval by PDE_DATA().
198+
*
199+
* Create a network-namespaced proc file in the @parent directory with the
200+
* specified @name and @mode that allows reading of a file that displays a
201+
* single element rather than a series and also provides for the file accepting
202+
* writes that have some arbitrary effect.
203+
*
204+
* The @show function is called to extract the readable content via the
205+
* seq_file interface.
206+
*
207+
* The @write function is called with the data copied into a kernel space
208+
* scratch buffer and has a NUL appended for convenience. The buffer may be
209+
* modified by the @write function. @write should return 0 on success.
210+
*
211+
* The @data value is accessible from the @show and @write functions by calling
212+
* PDE_DATA() on the file inode. The network namespace must be accessed by
213+
* calling seq_file_single_net() on the seq_file struct.
214+
*/
215+
struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
216+
struct proc_dir_entry *parent,
217+
int (*show)(struct seq_file *, void *),
218+
proc_write_t write,
219+
void *data)
220+
{
221+
struct proc_dir_entry *p;
222+
223+
p = proc_create_reg(name, mode, &parent, data);
224+
if (!p)
225+
return NULL;
226+
p->proc_fops = &proc_net_single_fops;
227+
p->single_show = show;
228+
p->write = write;
229+
return proc_register(parent, p);
230+
}
231+
EXPORT_SYMBOL_GPL(proc_create_net_single_write);
232+
141233
static struct net *get_proc_task_net(struct inode *dir)
142234
{
143235
struct task_struct *task;

include/linux/proc_fs.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ struct seq_operations;
1414

1515
#ifdef CONFIG_PROC_FS
1616

17+
typedef int (*proc_write_t)(struct file *, char *, size_t);
18+
1719
extern void proc_root_init(void);
1820
extern void proc_flush_task(struct task_struct *);
1921

@@ -61,6 +63,16 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
6163
struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
6264
struct proc_dir_entry *parent,
6365
int (*show)(struct seq_file *, void *), void *data);
66+
struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
67+
struct proc_dir_entry *parent,
68+
const struct seq_operations *ops,
69+
proc_write_t write,
70+
unsigned int state_size, void *data);
71+
struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
72+
struct proc_dir_entry *parent,
73+
int (*show)(struct seq_file *, void *),
74+
proc_write_t write,
75+
void *data);
6476

6577
#else /* CONFIG_PROC_FS */
6678

0 commit comments

Comments
 (0)