]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * linux/fs/proc/inode.c | |
4 | * | |
5 | * Copyright (C) 1991, 1992 Linus Torvalds | |
6 | */ | |
7 | ||
efb1a57d | 8 | #include <linux/cache.h> |
1da177e4 LT |
9 | #include <linux/time.h> |
10 | #include <linux/proc_fs.h> | |
11 | #include <linux/kernel.h> | |
97412950 | 12 | #include <linux/pid_namespace.h> |
1da177e4 LT |
13 | #include <linux/mm.h> |
14 | #include <linux/string.h> | |
15 | #include <linux/stat.h> | |
786d7e16 | 16 | #include <linux/completion.h> |
dd23aae4 | 17 | #include <linux/poll.h> |
87ebdc00 | 18 | #include <linux/printk.h> |
1da177e4 LT |
19 | #include <linux/file.h> |
20 | #include <linux/limits.h> | |
21 | #include <linux/init.h> | |
22 | #include <linux/module.h> | |
9043476f | 23 | #include <linux/sysctl.h> |
97412950 | 24 | #include <linux/seq_file.h> |
5a0e3ad6 | 25 | #include <linux/slab.h> |
97412950 | 26 | #include <linux/mount.h> |
1da177e4 | 27 | |
7c0f6ba6 | 28 | #include <linux/uaccess.h> |
1da177e4 | 29 | |
fee781e6 | 30 | #include "internal.h" |
1da177e4 | 31 | |
8267952b | 32 | static void proc_evict_inode(struct inode *inode) |
1da177e4 LT |
33 | { |
34 | struct proc_dir_entry *de; | |
dfef6dcd | 35 | struct ctl_table_header *head; |
1da177e4 | 36 | |
91b0abe3 | 37 | truncate_inode_pages_final(&inode->i_data); |
dbd5768f | 38 | clear_inode(inode); |
fef26658 | 39 | |
99f89551 | 40 | /* Stop tracking associated processes */ |
13b41b09 | 41 | put_pid(PROC_I(inode)->pid); |
1da177e4 LT |
42 | |
43 | /* Let go of any associated proc directory entry */ | |
6bee55f9 | 44 | de = PDE(inode); |
99b76233 | 45 | if (de) |
135d5655 | 46 | pde_put(de); |
d6cffbbe | 47 | |
dfef6dcd AV |
48 | head = PROC_I(inode)->sysctl; |
49 | if (head) { | |
1c44dbc8 | 50 | RCU_INIT_POINTER(PROC_I(inode)->sysctl, NULL); |
d6cffbbe | 51 | proc_sys_evict_inode(inode, head); |
dfef6dcd | 52 | } |
1da177e4 LT |
53 | } |
54 | ||
efb1a57d | 55 | static struct kmem_cache *proc_inode_cachep __ro_after_init; |
195b8cf0 | 56 | static struct kmem_cache *pde_opener_cache __ro_after_init; |
1da177e4 LT |
57 | |
58 | static struct inode *proc_alloc_inode(struct super_block *sb) | |
59 | { | |
60 | struct proc_inode *ei; | |
1da177e4 | 61 | |
f245e1c1 | 62 | ei = kmem_cache_alloc(proc_inode_cachep, GFP_KERNEL); |
1da177e4 LT |
63 | if (!ei) |
64 | return NULL; | |
13b41b09 | 65 | ei->pid = NULL; |
aed7a6c4 | 66 | ei->fd = 0; |
1da177e4 LT |
67 | ei->op.proc_get_link = NULL; |
68 | ei->pde = NULL; | |
9043476f AV |
69 | ei->sysctl = NULL; |
70 | ei->sysctl_entry = NULL; | |
0afa5ca8 | 71 | INIT_HLIST_NODE(&ei->sibling_inodes); |
3d3d35b1 | 72 | ei->ns_ops = NULL; |
230f72e9 | 73 | return &ei->vfs_inode; |
1da177e4 LT |
74 | } |
75 | ||
4aa6b55c | 76 | static void proc_free_inode(struct inode *inode) |
1da177e4 LT |
77 | { |
78 | kmem_cache_free(proc_inode_cachep, PROC_I(inode)); | |
79 | } | |
80 | ||
51cc5068 | 81 | static void init_once(void *foo) |
1da177e4 LT |
82 | { |
83 | struct proc_inode *ei = (struct proc_inode *) foo; | |
84 | ||
a35afb83 | 85 | inode_init_once(&ei->vfs_inode); |
1da177e4 | 86 | } |
20c2df83 | 87 | |
195b8cf0 | 88 | void __init proc_init_kmemcache(void) |
1da177e4 LT |
89 | { |
90 | proc_inode_cachep = kmem_cache_create("proc_inode_cache", | |
91 | sizeof(struct proc_inode), | |
fffb60f9 | 92 | 0, (SLAB_RECLAIM_ACCOUNT| |
5d097056 VD |
93 | SLAB_MEM_SPREAD|SLAB_ACCOUNT| |
94 | SLAB_PANIC), | |
20c2df83 | 95 | init_once); |
195b8cf0 AD |
96 | pde_opener_cache = |
97 | kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0, | |
2acddbe8 | 98 | SLAB_ACCOUNT|SLAB_PANIC, NULL); |
b4884f23 | 99 | proc_dir_entry_cache = kmem_cache_create_usercopy( |
2d6e4e82 AD |
100 | "proc_dir_entry", SIZEOF_PDE, 0, SLAB_PANIC, |
101 | offsetof(struct proc_dir_entry, inline_name), | |
102 | SIZEOF_PDE_INLINE_NAME, NULL); | |
103 | BUILD_BUG_ON(sizeof(struct proc_dir_entry) >= SIZEOF_PDE); | |
1da177e4 LT |
104 | } |
105 | ||
97412950 VK |
106 | static int proc_show_options(struct seq_file *seq, struct dentry *root) |
107 | { | |
0499680a VK |
108 | struct super_block *sb = root->d_sb; |
109 | struct pid_namespace *pid = sb->s_fs_info; | |
110 | ||
dcb0f222 EB |
111 | if (!gid_eq(pid->pid_gid, GLOBAL_ROOT_GID)) |
112 | seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, pid->pid_gid)); | |
796f571b | 113 | if (pid->hide_pid != HIDEPID_OFF) |
0499680a VK |
114 | seq_printf(seq, ",hidepid=%u", pid->hide_pid); |
115 | ||
97412950 VK |
116 | return 0; |
117 | } | |
118 | ||
60a3c3a5 | 119 | const struct super_operations proc_sops = { |
1da177e4 | 120 | .alloc_inode = proc_alloc_inode, |
4aa6b55c | 121 | .free_inode = proc_free_inode, |
1da177e4 | 122 | .drop_inode = generic_delete_inode, |
8267952b | 123 | .evict_inode = proc_evict_inode, |
1da177e4 | 124 | .statfs = simple_statfs, |
97412950 | 125 | .show_options = proc_show_options, |
1da177e4 LT |
126 | }; |
127 | ||
866ad9a7 AV |
128 | enum {BIAS = -1U<<31}; |
129 | ||
130 | static inline int use_pde(struct proc_dir_entry *pde) | |
131 | { | |
15b158b4 | 132 | return likely(atomic_inc_unless_negative(&pde->in_use)); |
881adb85 AD |
133 | } |
134 | ||
866ad9a7 | 135 | static void unuse_pde(struct proc_dir_entry *pde) |
881adb85 | 136 | { |
15b158b4 | 137 | if (unlikely(atomic_dec_return(&pde->in_use) == BIAS)) |
05c0ae21 | 138 | complete(pde->pde_unload_completion); |
786d7e16 AD |
139 | } |
140 | ||
2f897424 | 141 | /* pde is locked on entry, unlocked on exit */ |
ca469f35 AV |
142 | static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo) |
143 | { | |
492b2da6 AD |
144 | /* |
145 | * close() (proc_reg_release()) can't delete an entry and proceed: | |
146 | * ->release hook needs to be available at the right moment. | |
147 | * | |
148 | * rmmod (remove_proc_entry() et al) can't delete an entry and proceed: | |
149 | * "struct file" needs to be available at the right moment. | |
150 | * | |
151 | * Therefore, first process to enter this function does ->release() and | |
152 | * signals its completion to the other process which does nothing. | |
153 | */ | |
05c0ae21 | 154 | if (pdeo->closing) { |
ca469f35 | 155 | /* somebody else is doing that, just wait */ |
05c0ae21 AV |
156 | DECLARE_COMPLETION_ONSTACK(c); |
157 | pdeo->c = &c; | |
ca469f35 | 158 | spin_unlock(&pde->pde_unload_lock); |
05c0ae21 | 159 | wait_for_completion(&c); |
ca469f35 AV |
160 | } else { |
161 | struct file *file; | |
2f897424 AD |
162 | struct completion *c; |
163 | ||
f5887c71 | 164 | pdeo->closing = true; |
ca469f35 AV |
165 | spin_unlock(&pde->pde_unload_lock); |
166 | file = pdeo->file; | |
d56c0d45 | 167 | pde->proc_ops->proc_release(file_inode(file), file); |
ca469f35 | 168 | spin_lock(&pde->pde_unload_lock); |
492b2da6 | 169 | /* After ->release. */ |
06a0c417 | 170 | list_del(&pdeo->lh); |
2f897424 AD |
171 | c = pdeo->c; |
172 | spin_unlock(&pde->pde_unload_lock); | |
173 | if (unlikely(c)) | |
174 | complete(c); | |
195b8cf0 | 175 | kmem_cache_free(pde_opener_cache, pdeo); |
05c0ae21 | 176 | } |
ca469f35 AV |
177 | } |
178 | ||
866ad9a7 | 179 | void proc_entry_rundown(struct proc_dir_entry *de) |
786d7e16 | 180 | { |
05c0ae21 | 181 | DECLARE_COMPLETION_ONSTACK(c); |
866ad9a7 | 182 | /* Wait until all existing callers into module are done. */ |
05c0ae21 AV |
183 | de->pde_unload_completion = &c; |
184 | if (atomic_add_return(BIAS, &de->in_use) != BIAS) | |
185 | wait_for_completion(&c); | |
786d7e16 | 186 | |
492b2da6 AD |
187 | /* ->pde_openers list can't grow from now on. */ |
188 | ||
05c0ae21 | 189 | spin_lock(&de->pde_unload_lock); |
866ad9a7 AV |
190 | while (!list_empty(&de->pde_openers)) { |
191 | struct pde_opener *pdeo; | |
866ad9a7 | 192 | pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); |
ca469f35 | 193 | close_pdeo(de, pdeo); |
2f897424 | 194 | spin_lock(&de->pde_unload_lock); |
866ad9a7 AV |
195 | } |
196 | spin_unlock(&de->pde_unload_lock); | |
786d7e16 AD |
197 | } |
198 | ||
866ad9a7 AV |
199 | static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) |
200 | { | |
201 | struct proc_dir_entry *pde = PDE(file_inode(file)); | |
202 | loff_t rv = -EINVAL; | |
203 | if (use_pde(pde)) { | |
d56c0d45 | 204 | typeof_member(struct proc_ops, proc_lseek) lseek; |
9af27b28 | 205 | |
d56c0d45 AD |
206 | lseek = pde->proc_ops->proc_lseek; |
207 | if (!lseek) | |
208 | lseek = default_llseek; | |
209 | rv = lseek(file, offset, whence); | |
866ad9a7 AV |
210 | unuse_pde(pde); |
211 | } | |
786d7e16 AD |
212 | return rv; |
213 | } | |
214 | ||
866ad9a7 | 215 | static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
786d7e16 | 216 | { |
496ad9aa | 217 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
786d7e16 | 218 | ssize_t rv = -EIO; |
866ad9a7 | 219 | if (use_pde(pde)) { |
d56c0d45 | 220 | typeof_member(struct proc_ops, proc_read) read; |
9af27b28 | 221 | |
d56c0d45 | 222 | read = pde->proc_ops->proc_read; |
866ad9a7 AV |
223 | if (read) |
224 | rv = read(file, buf, count, ppos); | |
225 | unuse_pde(pde); | |
786d7e16 | 226 | } |
866ad9a7 AV |
227 | return rv; |
228 | } | |
786d7e16 | 229 | |
866ad9a7 AV |
230 | static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) |
231 | { | |
866ad9a7 AV |
232 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
233 | ssize_t rv = -EIO; | |
234 | if (use_pde(pde)) { | |
d56c0d45 | 235 | typeof_member(struct proc_ops, proc_write) write; |
9af27b28 | 236 | |
d56c0d45 | 237 | write = pde->proc_ops->proc_write; |
866ad9a7 AV |
238 | if (write) |
239 | rv = write(file, buf, count, ppos); | |
240 | unuse_pde(pde); | |
241 | } | |
786d7e16 AD |
242 | return rv; |
243 | } | |
244 | ||
076ccb76 | 245 | static __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts) |
786d7e16 | 246 | { |
496ad9aa | 247 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
e6c8adca | 248 | __poll_t rv = DEFAULT_POLLMASK; |
866ad9a7 | 249 | if (use_pde(pde)) { |
d56c0d45 | 250 | typeof_member(struct proc_ops, proc_poll) poll; |
9af27b28 | 251 | |
d56c0d45 | 252 | poll = pde->proc_ops->proc_poll; |
866ad9a7 AV |
253 | if (poll) |
254 | rv = poll(file, pts); | |
255 | unuse_pde(pde); | |
786d7e16 | 256 | } |
786d7e16 AD |
257 | return rv; |
258 | } | |
259 | ||
260 | static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
261 | { | |
496ad9aa | 262 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
786d7e16 | 263 | long rv = -ENOTTY; |
866ad9a7 | 264 | if (use_pde(pde)) { |
d56c0d45 | 265 | typeof_member(struct proc_ops, proc_ioctl) ioctl; |
9af27b28 | 266 | |
d56c0d45 | 267 | ioctl = pde->proc_ops->proc_ioctl; |
866ad9a7 AV |
268 | if (ioctl) |
269 | rv = ioctl(file, cmd, arg); | |
270 | unuse_pde(pde); | |
786d7e16 | 271 | } |
786d7e16 AD |
272 | return rv; |
273 | } | |
274 | ||
275 | #ifdef CONFIG_COMPAT | |
276 | static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
277 | { | |
496ad9aa | 278 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
786d7e16 | 279 | long rv = -ENOTTY; |
866ad9a7 | 280 | if (use_pde(pde)) { |
d56c0d45 | 281 | typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl; |
9af27b28 | 282 | |
d56c0d45 | 283 | compat_ioctl = pde->proc_ops->proc_compat_ioctl; |
866ad9a7 AV |
284 | if (compat_ioctl) |
285 | rv = compat_ioctl(file, cmd, arg); | |
286 | unuse_pde(pde); | |
786d7e16 | 287 | } |
786d7e16 AD |
288 | return rv; |
289 | } | |
290 | #endif | |
291 | ||
292 | static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) | |
293 | { | |
496ad9aa | 294 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
786d7e16 | 295 | int rv = -EIO; |
866ad9a7 | 296 | if (use_pde(pde)) { |
d56c0d45 | 297 | typeof_member(struct proc_ops, proc_mmap) mmap; |
9af27b28 | 298 | |
d56c0d45 | 299 | mmap = pde->proc_ops->proc_mmap; |
866ad9a7 AV |
300 | if (mmap) |
301 | rv = mmap(file, vma); | |
302 | unuse_pde(pde); | |
786d7e16 | 303 | } |
786d7e16 AD |
304 | return rv; |
305 | } | |
306 | ||
5721cf84 HD |
307 | static unsigned long |
308 | proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr, | |
309 | unsigned long len, unsigned long pgoff, | |
310 | unsigned long flags) | |
c4fe2448 AD |
311 | { |
312 | struct proc_dir_entry *pde = PDE(file_inode(file)); | |
2cbe3b0a | 313 | unsigned long rv = -EIO; |
ae5758a1 | 314 | |
c4fe2448 | 315 | if (use_pde(pde)) { |
d56c0d45 | 316 | typeof_member(struct proc_ops, proc_get_unmapped_area) get_area; |
ae5758a1 | 317 | |
d56c0d45 | 318 | get_area = pde->proc_ops->proc_get_unmapped_area; |
fad1a86e | 319 | #ifdef CONFIG_MMU |
ae5758a1 JB |
320 | if (!get_area) |
321 | get_area = current->mm->get_unmapped_area; | |
fad1a86e | 322 | #endif |
ae5758a1 | 323 | |
5721cf84 HD |
324 | if (get_area) |
325 | rv = get_area(file, orig_addr, len, pgoff, flags); | |
ae5758a1 JB |
326 | else |
327 | rv = orig_addr; | |
c4fe2448 AD |
328 | unuse_pde(pde); |
329 | } | |
330 | return rv; | |
331 | } | |
332 | ||
786d7e16 AD |
333 | static int proc_reg_open(struct inode *inode, struct file *file) |
334 | { | |
335 | struct proc_dir_entry *pde = PDE(inode); | |
336 | int rv = 0; | |
d56c0d45 AD |
337 | typeof_member(struct proc_ops, proc_open) open; |
338 | typeof_member(struct proc_ops, proc_release) release; | |
881adb85 AD |
339 | struct pde_opener *pdeo; |
340 | ||
341 | /* | |
492b2da6 AD |
342 | * Ensure that |
343 | * 1) PDE's ->release hook will be called no matter what | |
344 | * either normally by close()/->release, or forcefully by | |
345 | * rmmod/remove_proc_entry. | |
346 | * | |
347 | * 2) rmmod isn't blocked by opening file in /proc and sitting on | |
348 | * the descriptor (including "rmmod foo </proc/foo" scenario). | |
881adb85 | 349 | * |
492b2da6 | 350 | * Save every "struct file" with custom ->release hook. |
881adb85 | 351 | */ |
e7a6e291 | 352 | if (!use_pde(pde)) |
d2857e79 | 353 | return -ENOENT; |
e7a6e291 | 354 | |
d56c0d45 | 355 | release = pde->proc_ops->proc_release; |
e7a6e291 | 356 | if (release) { |
195b8cf0 | 357 | pdeo = kmem_cache_alloc(pde_opener_cache, GFP_KERNEL); |
e7a6e291 AD |
358 | if (!pdeo) { |
359 | rv = -ENOMEM; | |
360 | goto out_unuse; | |
361 | } | |
362 | } | |
786d7e16 | 363 | |
d56c0d45 | 364 | open = pde->proc_ops->proc_open; |
786d7e16 AD |
365 | if (open) |
366 | rv = open(inode, file); | |
367 | ||
e7a6e291 AD |
368 | if (release) { |
369 | if (rv == 0) { | |
370 | /* To know what to release. */ | |
371 | pdeo->file = file; | |
372 | pdeo->closing = false; | |
373 | pdeo->c = NULL; | |
374 | spin_lock(&pde->pde_unload_lock); | |
375 | list_add(&pdeo->lh, &pde->pde_openers); | |
376 | spin_unlock(&pde->pde_unload_lock); | |
377 | } else | |
195b8cf0 | 378 | kmem_cache_free(pde_opener_cache, pdeo); |
e7a6e291 | 379 | } |
05c0ae21 | 380 | |
e7a6e291 | 381 | out_unuse: |
05c0ae21 | 382 | unuse_pde(pde); |
786d7e16 AD |
383 | return rv; |
384 | } | |
385 | ||
386 | static int proc_reg_release(struct inode *inode, struct file *file) | |
387 | { | |
388 | struct proc_dir_entry *pde = PDE(inode); | |
881adb85 | 389 | struct pde_opener *pdeo; |
786d7e16 | 390 | spin_lock(&pde->pde_unload_lock); |
ca469f35 AV |
391 | list_for_each_entry(pdeo, &pde->pde_openers, lh) { |
392 | if (pdeo->file == file) { | |
393 | close_pdeo(pde, pdeo); | |
2f897424 | 394 | return 0; |
ca469f35 | 395 | } |
881adb85 | 396 | } |
786d7e16 | 397 | spin_unlock(&pde->pde_unload_lock); |
ca469f35 | 398 | return 0; |
786d7e16 AD |
399 | } |
400 | ||
401 | static const struct file_operations proc_reg_file_ops = { | |
402 | .llseek = proc_reg_llseek, | |
403 | .read = proc_reg_read, | |
404 | .write = proc_reg_write, | |
405 | .poll = proc_reg_poll, | |
406 | .unlocked_ioctl = proc_reg_unlocked_ioctl, | |
407 | #ifdef CONFIG_COMPAT | |
408 | .compat_ioctl = proc_reg_compat_ioctl, | |
409 | #endif | |
410 | .mmap = proc_reg_mmap, | |
c4fe2448 | 411 | .get_unmapped_area = proc_reg_get_unmapped_area, |
786d7e16 AD |
412 | .open = proc_reg_open, |
413 | .release = proc_reg_release, | |
414 | }; | |
415 | ||
778f3dd5 DM |
416 | #ifdef CONFIG_COMPAT |
417 | static const struct file_operations proc_reg_file_ops_no_compat = { | |
418 | .llseek = proc_reg_llseek, | |
419 | .read = proc_reg_read, | |
420 | .write = proc_reg_write, | |
421 | .poll = proc_reg_poll, | |
422 | .unlocked_ioctl = proc_reg_unlocked_ioctl, | |
423 | .mmap = proc_reg_mmap, | |
c4fe2448 | 424 | .get_unmapped_area = proc_reg_get_unmapped_area, |
778f3dd5 DM |
425 | .open = proc_reg_open, |
426 | .release = proc_reg_release, | |
427 | }; | |
428 | #endif | |
429 | ||
fceef393 AV |
430 | static void proc_put_link(void *p) |
431 | { | |
432 | unuse_pde(p); | |
433 | } | |
434 | ||
6b255391 | 435 | static const char *proc_get_link(struct dentry *dentry, |
fceef393 AV |
436 | struct inode *inode, |
437 | struct delayed_call *done) | |
7e0e953b | 438 | { |
6b255391 | 439 | struct proc_dir_entry *pde = PDE(inode); |
15b158b4 | 440 | if (!use_pde(pde)) |
7e0e953b | 441 | return ERR_PTR(-EINVAL); |
fceef393 | 442 | set_delayed_call(done, proc_put_link, pde); |
680baacb | 443 | return pde->data; |
7e0e953b AV |
444 | } |
445 | ||
7e0e953b | 446 | const struct inode_operations proc_link_inode_operations = { |
6b255391 | 447 | .get_link = proc_get_link, |
7e0e953b AV |
448 | }; |
449 | ||
6d1b6e4e | 450 | struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) |
1da177e4 | 451 | { |
51f0885e | 452 | struct inode *inode = new_inode_pseudo(sb); |
1da177e4 | 453 | |
51f0885e LT |
454 | if (inode) { |
455 | inode->i_ino = de->low_ino; | |
078cd827 | 456 | inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); |
a1d4aebb | 457 | PROC_I(inode)->pde = de; |
5e971dce | 458 | |
eb6d38d5 EB |
459 | if (is_empty_pde(de)) { |
460 | make_empty_dir_inode(inode); | |
461 | return inode; | |
462 | } | |
5e971dce AD |
463 | if (de->mode) { |
464 | inode->i_mode = de->mode; | |
465 | inode->i_uid = de->uid; | |
466 | inode->i_gid = de->gid; | |
467 | } | |
468 | if (de->size) | |
469 | inode->i_size = de->size; | |
470 | if (de->nlink) | |
bfe86848 | 471 | set_nlink(inode, de->nlink); |
d56c0d45 AD |
472 | |
473 | if (S_ISREG(inode->i_mode)) { | |
474 | inode->i_op = de->proc_iops; | |
475 | inode->i_fop = &proc_reg_file_ops; | |
778f3dd5 | 476 | #ifdef CONFIG_COMPAT |
d56c0d45 AD |
477 | if (!de->proc_ops->proc_compat_ioctl) { |
478 | inode->i_fop = &proc_reg_file_ops_no_compat; | |
778f3dd5 | 479 | } |
d56c0d45 AD |
480 | #endif |
481 | } else if (S_ISDIR(inode->i_mode)) { | |
482 | inode->i_op = de->proc_iops; | |
483 | inode->i_fop = de->proc_dir_ops; | |
484 | } else if (S_ISLNK(inode->i_mode)) { | |
485 | inode->i_op = de->proc_iops; | |
486 | inode->i_fop = NULL; | |
487 | } else | |
488 | BUG(); | |
99b76233 | 489 | } else |
135d5655 | 490 | pde_put(de); |
1da177e4 | 491 | return inode; |
d3d009cb | 492 | } |