blob: 0c0bd14fa54a7d147dc48a1547a4f0565adc9fb8 [file] [log] [blame]
/* Reduced and adapted from Linux: fs/proc/inode.c: proc_reg_open
(GPL v2.0). */
/* Types. */
typedef unsigned char u8;
typedef _Bool bool;
typedef unsigned int gfp_t;
struct file;
struct kmem_cache;
struct proc_dir_entry;
struct inode { /* [...snip...] */ };
enum {
PROC_ENTRY_PERMANENT = 1U << 0,
};
struct proc_ops {
/* [...snip...] */
int (*proc_open)(struct inode *, struct file *);
/* [...snip...] */
int (*proc_release)(struct inode *, struct file *);
/* [...snip...] */
};
struct proc_dir_entry {
/* [...snip...] */
struct completion *pde_unload_completion;
/* [...snip...] */
union {
const struct proc_ops *proc_ops;
const struct file_operations *proc_dir_ops;
};
/* [...snip...] */
u8 flags;
/* [...snip...] */
};
struct pde_opener {
/* [...snip...] */
struct file *file;
/* [...snip...] */
};
struct proc_inode {
/* [...snip...] */
struct proc_dir_entry *pde;
/* [...snip...] */
struct inode vfs_inode;
};
/* Data. */
static struct kmem_cache *pde_opener_cache __attribute__((__section__(".data..ro_after_init")));
/* Functions. */
void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags) __attribute__((__malloc__));
void kmem_cache_free(struct kmem_cache *, void *);
static inline bool pde_is_permanent(const struct proc_dir_entry *pde)
{
return pde->flags & PROC_ENTRY_PERMANENT;
}
static inline struct proc_inode *PROC_I(const struct inode *inode)
{
void *__mptr = (void *)(inode);
return ((struct proc_inode *)(__mptr - __builtin_offsetof(struct proc_inode, vfs_inode)));
}
static inline struct proc_dir_entry *PDE(const struct inode *inode)
{
return PROC_I(inode)->pde;
}
/* We don't want to emit bogus use of uninitialized value 'pdeo'
warnings from -Wanalyzer-use-of-uninitialized-value in this function;
these would require following infeasible paths in which "release" is
first NULL (to avoid the initialization of "pdeo") and then is non-NULL
(to access "pdeo").
"release" is sufficiently complicated in this function to hit the
complexity limit for symbolic values during enode exploration. */
static int proc_reg_open(struct inode *inode, struct file *file)
{
struct proc_dir_entry *pde = PDE(inode);
int rv = 0;
typeof(((struct proc_ops*)0)->proc_open) open;
typeof(((struct proc_ops*)0)->proc_release) release;
struct pde_opener *pdeo;
if (pde_is_permanent(pde)) {
open = pde->proc_ops->proc_open;
if (open)
rv = open(inode, file);
return rv;
}
/* [...snip...] */
release = pde->proc_ops->proc_release;
if (release) {
pdeo = kmem_cache_alloc(pde_opener_cache,
((( gfp_t)(0x400u|0x800u))
| (( gfp_t)0x40u)
| (( gfp_t)0x80u)));
if (!pdeo) {
rv = -12;
goto out_unuse;
}
}
open = pde->proc_ops->proc_open;
if (open)
rv = open(inode, file);
if (release) {
if (rv == 0) {
pdeo->file = file; /* { dg-bogus "uninit" } */
/* [...snip...] */
} else
kmem_cache_free(pde_opener_cache, pdeo); /* { dg-bogus "uninit" } */
}
out_unuse:
/* [...snip...] */
return rv;
}