proc/kcore: clean up ELF header generation
Currently, the ELF file header, program headers, and note segment are allocated all at once, in some icky code dating back to 2.3. Programs tend to read the file header, then the program headers, then the note segment, all separately, so this is a waste of effort. It's cleaner and more efficient to handle the three separately. Link: http://lkml.kernel.org/r/19c92cbad0e11f6103ff3274b2e7a7e51a1eb74b.1531953780.git.osandov@fb.com Signed-off-by: Omar Sandoval <osandov@fb.com> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Bhupesh Sharma <bhsharma@redhat.com> Cc: Eric Biederman <ebiederm@xmission.com> Cc: James Morse <james.morse@arm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
3673fb08db
commit
37e949bd52
342
fs/proc/kcore.c
342
fs/proc/kcore.c
|
@ -49,15 +49,6 @@ static struct proc_dir_entry *proc_root_kcore;
|
||||||
#define kc_offset_to_vaddr(o) ((o) + PAGE_OFFSET)
|
#define kc_offset_to_vaddr(o) ((o) + PAGE_OFFSET)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* An ELF note in memory */
|
|
||||||
struct memelfnote
|
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
int type;
|
|
||||||
unsigned int datasz;
|
|
||||||
void *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
static LIST_HEAD(kclist_head);
|
static LIST_HEAD(kclist_head);
|
||||||
static DECLARE_RWSEM(kclist_lock);
|
static DECLARE_RWSEM(kclist_lock);
|
||||||
static int kcore_need_update = 1;
|
static int kcore_need_update = 1;
|
||||||
|
@ -73,7 +64,8 @@ void __init kclist_add(struct kcore_list *new, void *addr, size_t size,
|
||||||
list_add_tail(&new->list, &kclist_head);
|
list_add_tail(&new->list, &kclist_head);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t get_kcore_size(int *nphdr, size_t *elf_buflen)
|
static size_t get_kcore_size(int *nphdr, size_t *phdrs_len, size_t *notes_len,
|
||||||
|
size_t *data_offset)
|
||||||
{
|
{
|
||||||
size_t try, size;
|
size_t try, size;
|
||||||
struct kcore_list *m;
|
struct kcore_list *m;
|
||||||
|
@ -87,15 +79,15 @@ static size_t get_kcore_size(int *nphdr, size_t *elf_buflen)
|
||||||
size = try;
|
size = try;
|
||||||
*nphdr = *nphdr + 1;
|
*nphdr = *nphdr + 1;
|
||||||
}
|
}
|
||||||
*elf_buflen = sizeof(struct elfhdr) +
|
|
||||||
(*nphdr + 2)*sizeof(struct elf_phdr) +
|
*phdrs_len = *nphdr * sizeof(struct elf_phdr);
|
||||||
3 * ((sizeof(struct elf_note)) +
|
*notes_len = (3 * (sizeof(struct elf_note) + ALIGN(sizeof(CORE_STR), 4)) +
|
||||||
roundup(sizeof(CORE_STR), 4)) +
|
ALIGN(sizeof(struct elf_prstatus), 4) +
|
||||||
roundup(sizeof(struct elf_prstatus), 4) +
|
ALIGN(sizeof(struct elf_prpsinfo), 4) +
|
||||||
roundup(sizeof(struct elf_prpsinfo), 4) +
|
ALIGN(arch_task_struct_size, 4));
|
||||||
roundup(arch_task_struct_size, 4);
|
*data_offset = PAGE_ALIGN(sizeof(struct elfhdr) + *phdrs_len +
|
||||||
*elf_buflen = PAGE_ALIGN(*elf_buflen);
|
*notes_len);
|
||||||
return size + *elf_buflen;
|
return *data_offset + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HIGHMEM
|
#ifdef CONFIG_HIGHMEM
|
||||||
|
@ -241,7 +233,7 @@ static int kcore_update_ram(void)
|
||||||
LIST_HEAD(list);
|
LIST_HEAD(list);
|
||||||
LIST_HEAD(garbage);
|
LIST_HEAD(garbage);
|
||||||
int nphdr;
|
int nphdr;
|
||||||
size_t size;
|
size_t phdrs_len, notes_len, data_offset;
|
||||||
struct kcore_list *tmp, *pos;
|
struct kcore_list *tmp, *pos;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -263,7 +255,8 @@ static int kcore_update_ram(void)
|
||||||
}
|
}
|
||||||
list_splice_tail(&list, &kclist_head);
|
list_splice_tail(&list, &kclist_head);
|
||||||
|
|
||||||
proc_root_kcore->size = get_kcore_size(&nphdr, &size);
|
proc_root_kcore->size = get_kcore_size(&nphdr, &phdrs_len, ¬es_len,
|
||||||
|
&data_offset);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
up_write(&kclist_lock);
|
up_write(&kclist_lock);
|
||||||
|
@ -274,108 +267,94 @@ static int kcore_update_ram(void)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
static void append_kcore_note(char *notes, size_t *i, const char *name,
|
||||||
/*
|
unsigned int type, const void *desc,
|
||||||
* determine size of ELF note
|
size_t descsz)
|
||||||
*/
|
|
||||||
static int notesize(struct memelfnote *en)
|
|
||||||
{
|
{
|
||||||
int sz;
|
struct elf_note *note = (struct elf_note *)¬es[*i];
|
||||||
|
|
||||||
sz = sizeof(struct elf_note);
|
note->n_namesz = strlen(name) + 1;
|
||||||
sz += roundup((strlen(en->name) + 1), 4);
|
note->n_descsz = descsz;
|
||||||
sz += roundup(en->datasz, 4);
|
note->n_type = type;
|
||||||
|
*i += sizeof(*note);
|
||||||
|
memcpy(¬es[*i], name, note->n_namesz);
|
||||||
|
*i = ALIGN(*i + note->n_namesz, 4);
|
||||||
|
memcpy(¬es[*i], desc, descsz);
|
||||||
|
*i = ALIGN(*i + descsz, 4);
|
||||||
|
}
|
||||||
|
|
||||||
return sz;
|
static ssize_t
|
||||||
} /* end notesize() */
|
read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/*
|
|
||||||
* store a note in the header buffer
|
|
||||||
*/
|
|
||||||
static char *storenote(struct memelfnote *men, char *bufp)
|
|
||||||
{
|
{
|
||||||
struct elf_note en;
|
char *buf = file->private_data;
|
||||||
|
size_t phdrs_offset, notes_offset, data_offset;
|
||||||
#define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
|
size_t phdrs_len, notes_len;
|
||||||
|
|
||||||
en.n_namesz = strlen(men->name) + 1;
|
|
||||||
en.n_descsz = men->datasz;
|
|
||||||
en.n_type = men->type;
|
|
||||||
|
|
||||||
DUMP_WRITE(&en, sizeof(en));
|
|
||||||
DUMP_WRITE(men->name, en.n_namesz);
|
|
||||||
|
|
||||||
/* XXX - cast from long long to long to avoid need for libgcc.a */
|
|
||||||
bufp = (char*) roundup((unsigned long)bufp,4);
|
|
||||||
DUMP_WRITE(men->data, men->datasz);
|
|
||||||
bufp = (char*) roundup((unsigned long)bufp,4);
|
|
||||||
|
|
||||||
#undef DUMP_WRITE
|
|
||||||
|
|
||||||
return bufp;
|
|
||||||
} /* end storenote() */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* store an ELF coredump header in the supplied buffer
|
|
||||||
* nphdr is the number of elf_phdr to insert
|
|
||||||
*/
|
|
||||||
static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)
|
|
||||||
{
|
|
||||||
struct elf_prstatus prstatus; /* NT_PRSTATUS */
|
|
||||||
struct elf_prpsinfo prpsinfo; /* NT_PRPSINFO */
|
|
||||||
struct elf_phdr *nhdr, *phdr;
|
|
||||||
struct elfhdr *elf;
|
|
||||||
struct memelfnote notes[3];
|
|
||||||
off_t offset = 0;
|
|
||||||
struct kcore_list *m;
|
struct kcore_list *m;
|
||||||
|
size_t tsz;
|
||||||
|
int nphdr;
|
||||||
|
unsigned long start;
|
||||||
|
size_t orig_buflen = buflen;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
/* setup ELF header */
|
down_read(&kclist_lock);
|
||||||
elf = (struct elfhdr *) bufp;
|
|
||||||
bufp += sizeof(struct elfhdr);
|
|
||||||
offset += sizeof(struct elfhdr);
|
|
||||||
memcpy(elf->e_ident, ELFMAG, SELFMAG);
|
|
||||||
elf->e_ident[EI_CLASS] = ELF_CLASS;
|
|
||||||
elf->e_ident[EI_DATA] = ELF_DATA;
|
|
||||||
elf->e_ident[EI_VERSION]= EV_CURRENT;
|
|
||||||
elf->e_ident[EI_OSABI] = ELF_OSABI;
|
|
||||||
memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
|
||||||
elf->e_type = ET_CORE;
|
|
||||||
elf->e_machine = ELF_ARCH;
|
|
||||||
elf->e_version = EV_CURRENT;
|
|
||||||
elf->e_entry = 0;
|
|
||||||
elf->e_phoff = sizeof(struct elfhdr);
|
|
||||||
elf->e_shoff = 0;
|
|
||||||
elf->e_flags = ELF_CORE_EFLAGS;
|
|
||||||
elf->e_ehsize = sizeof(struct elfhdr);
|
|
||||||
elf->e_phentsize= sizeof(struct elf_phdr);
|
|
||||||
elf->e_phnum = nphdr;
|
|
||||||
elf->e_shentsize= 0;
|
|
||||||
elf->e_shnum = 0;
|
|
||||||
elf->e_shstrndx = 0;
|
|
||||||
|
|
||||||
/* setup ELF PT_NOTE program header */
|
get_kcore_size(&nphdr, &phdrs_len, ¬es_len, &data_offset);
|
||||||
nhdr = (struct elf_phdr *) bufp;
|
phdrs_offset = sizeof(struct elfhdr);
|
||||||
bufp += sizeof(struct elf_phdr);
|
notes_offset = phdrs_offset + phdrs_len;
|
||||||
offset += sizeof(struct elf_phdr);
|
|
||||||
nhdr->p_type = PT_NOTE;
|
|
||||||
nhdr->p_offset = 0;
|
|
||||||
nhdr->p_vaddr = 0;
|
|
||||||
nhdr->p_paddr = 0;
|
|
||||||
nhdr->p_filesz = 0;
|
|
||||||
nhdr->p_memsz = 0;
|
|
||||||
nhdr->p_flags = 0;
|
|
||||||
nhdr->p_align = 0;
|
|
||||||
|
|
||||||
/* setup ELF PT_LOAD program header for every area */
|
/* ELF file header. */
|
||||||
|
if (buflen && *fpos < sizeof(struct elfhdr)) {
|
||||||
|
struct elfhdr ehdr = {
|
||||||
|
.e_ident = {
|
||||||
|
[EI_MAG0] = ELFMAG0,
|
||||||
|
[EI_MAG1] = ELFMAG1,
|
||||||
|
[EI_MAG2] = ELFMAG2,
|
||||||
|
[EI_MAG3] = ELFMAG3,
|
||||||
|
[EI_CLASS] = ELF_CLASS,
|
||||||
|
[EI_DATA] = ELF_DATA,
|
||||||
|
[EI_VERSION] = EV_CURRENT,
|
||||||
|
[EI_OSABI] = ELF_OSABI,
|
||||||
|
},
|
||||||
|
.e_type = ET_CORE,
|
||||||
|
.e_machine = ELF_ARCH,
|
||||||
|
.e_version = EV_CURRENT,
|
||||||
|
.e_phoff = sizeof(struct elfhdr),
|
||||||
|
.e_flags = ELF_CORE_EFLAGS,
|
||||||
|
.e_ehsize = sizeof(struct elfhdr),
|
||||||
|
.e_phentsize = sizeof(struct elf_phdr),
|
||||||
|
.e_phnum = nphdr,
|
||||||
|
};
|
||||||
|
|
||||||
|
tsz = min_t(size_t, buflen, sizeof(struct elfhdr) - *fpos);
|
||||||
|
if (copy_to_user(buffer, (char *)&ehdr + *fpos, tsz)) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer += tsz;
|
||||||
|
buflen -= tsz;
|
||||||
|
*fpos += tsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ELF program headers. */
|
||||||
|
if (buflen && *fpos < phdrs_offset + phdrs_len) {
|
||||||
|
struct elf_phdr *phdrs, *phdr;
|
||||||
|
|
||||||
|
phdrs = kzalloc(phdrs_len, GFP_KERNEL);
|
||||||
|
if (!phdrs) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
phdrs[0].p_type = PT_NOTE;
|
||||||
|
phdrs[0].p_offset = notes_offset;
|
||||||
|
phdrs[0].p_filesz = notes_len;
|
||||||
|
|
||||||
|
phdr = &phdrs[1];
|
||||||
list_for_each_entry(m, &kclist_head, list) {
|
list_for_each_entry(m, &kclist_head, list) {
|
||||||
phdr = (struct elf_phdr *) bufp;
|
|
||||||
bufp += sizeof(struct elf_phdr);
|
|
||||||
offset += sizeof(struct elf_phdr);
|
|
||||||
|
|
||||||
phdr->p_type = PT_LOAD;
|
phdr->p_type = PT_LOAD;
|
||||||
phdr->p_flags = PF_R | PF_W | PF_X;
|
phdr->p_flags = PF_R | PF_W | PF_X;
|
||||||
phdr->p_offset = kc_vaddr_to_offset(m->addr) + dataoff;
|
phdr->p_offset = kc_vaddr_to_offset(m->addr) + data_offset;
|
||||||
phdr->p_vaddr = (size_t)m->addr;
|
phdr->p_vaddr = (size_t)m->addr;
|
||||||
if (m->type == KCORE_RAM)
|
if (m->type == KCORE_RAM)
|
||||||
phdr->p_paddr = __pa(m->addr);
|
phdr->p_paddr = __pa(m->addr);
|
||||||
|
@ -385,117 +364,71 @@ static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)
|
||||||
phdr->p_paddr = (elf_addr_t)-1;
|
phdr->p_paddr = (elf_addr_t)-1;
|
||||||
phdr->p_filesz = phdr->p_memsz = m->size;
|
phdr->p_filesz = phdr->p_memsz = m->size;
|
||||||
phdr->p_align = PAGE_SIZE;
|
phdr->p_align = PAGE_SIZE;
|
||||||
|
phdr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
tsz = min_t(size_t, buflen, phdrs_offset + phdrs_len - *fpos);
|
||||||
* Set up the notes in similar form to SVR4 core dumps made
|
if (copy_to_user(buffer, (char *)phdrs + *fpos - phdrs_offset,
|
||||||
* with info from their /proc.
|
tsz)) {
|
||||||
*/
|
kfree(phdrs);
|
||||||
nhdr->p_offset = offset;
|
|
||||||
|
|
||||||
/* set up the process status */
|
|
||||||
notes[0].name = CORE_STR;
|
|
||||||
notes[0].type = NT_PRSTATUS;
|
|
||||||
notes[0].datasz = sizeof(struct elf_prstatus);
|
|
||||||
notes[0].data = &prstatus;
|
|
||||||
|
|
||||||
memset(&prstatus, 0, sizeof(struct elf_prstatus));
|
|
||||||
|
|
||||||
nhdr->p_filesz = notesize(¬es[0]);
|
|
||||||
bufp = storenote(¬es[0], bufp);
|
|
||||||
|
|
||||||
/* set up the process info */
|
|
||||||
notes[1].name = CORE_STR;
|
|
||||||
notes[1].type = NT_PRPSINFO;
|
|
||||||
notes[1].datasz = sizeof(struct elf_prpsinfo);
|
|
||||||
notes[1].data = &prpsinfo;
|
|
||||||
|
|
||||||
memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo));
|
|
||||||
prpsinfo.pr_state = 0;
|
|
||||||
prpsinfo.pr_sname = 'R';
|
|
||||||
prpsinfo.pr_zomb = 0;
|
|
||||||
|
|
||||||
strcpy(prpsinfo.pr_fname, "vmlinux");
|
|
||||||
strlcpy(prpsinfo.pr_psargs, saved_command_line, sizeof(prpsinfo.pr_psargs));
|
|
||||||
|
|
||||||
nhdr->p_filesz += notesize(¬es[1]);
|
|
||||||
bufp = storenote(¬es[1], bufp);
|
|
||||||
|
|
||||||
/* set up the task structure */
|
|
||||||
notes[2].name = CORE_STR;
|
|
||||||
notes[2].type = NT_TASKSTRUCT;
|
|
||||||
notes[2].datasz = arch_task_struct_size;
|
|
||||||
notes[2].data = current;
|
|
||||||
|
|
||||||
nhdr->p_filesz += notesize(¬es[2]);
|
|
||||||
bufp = storenote(¬es[2], bufp);
|
|
||||||
|
|
||||||
} /* end elf_kcore_store_hdr() */
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/*
|
|
||||||
* read from the ELF header and then kernel memory
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
|
|
||||||
{
|
|
||||||
char *buf = file->private_data;
|
|
||||||
size_t size, tsz;
|
|
||||||
size_t elf_buflen;
|
|
||||||
int nphdr;
|
|
||||||
unsigned long start;
|
|
||||||
size_t orig_buflen = buflen;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
down_read(&kclist_lock);
|
|
||||||
size = get_kcore_size(&nphdr, &elf_buflen);
|
|
||||||
|
|
||||||
if (buflen == 0 || *fpos >= size)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* trim buflen to not go beyond EOF */
|
|
||||||
if (buflen > size - *fpos)
|
|
||||||
buflen = size - *fpos;
|
|
||||||
|
|
||||||
/* construct an ELF core header if we'll need some of it */
|
|
||||||
if (*fpos < elf_buflen) {
|
|
||||||
char * elf_buf;
|
|
||||||
|
|
||||||
tsz = elf_buflen - *fpos;
|
|
||||||
if (buflen < tsz)
|
|
||||||
tsz = buflen;
|
|
||||||
elf_buf = kzalloc(elf_buflen, GFP_KERNEL);
|
|
||||||
if (!elf_buf) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen);
|
|
||||||
if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
|
|
||||||
kfree(elf_buf);
|
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
kfree(elf_buf);
|
kfree(phdrs);
|
||||||
|
|
||||||
|
buffer += tsz;
|
||||||
buflen -= tsz;
|
buflen -= tsz;
|
||||||
*fpos += tsz;
|
*fpos += tsz;
|
||||||
buffer += tsz;
|
}
|
||||||
|
|
||||||
/* leave now if filled buffer already */
|
/* ELF note segment. */
|
||||||
if (buflen == 0)
|
if (buflen && *fpos < notes_offset + notes_len) {
|
||||||
|
struct elf_prstatus prstatus = {};
|
||||||
|
struct elf_prpsinfo prpsinfo = {
|
||||||
|
.pr_sname = 'R',
|
||||||
|
.pr_fname = "vmlinux",
|
||||||
|
};
|
||||||
|
char *notes;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
strlcpy(prpsinfo.pr_psargs, saved_command_line,
|
||||||
|
sizeof(prpsinfo.pr_psargs));
|
||||||
|
|
||||||
|
notes = kzalloc(notes_len, GFP_KERNEL);
|
||||||
|
if (!notes) {
|
||||||
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
append_kcore_note(notes, &i, CORE_STR, NT_PRSTATUS, &prstatus,
|
||||||
|
sizeof(prstatus));
|
||||||
|
append_kcore_note(notes, &i, CORE_STR, NT_PRPSINFO, &prpsinfo,
|
||||||
|
sizeof(prpsinfo));
|
||||||
|
append_kcore_note(notes, &i, CORE_STR, NT_TASKSTRUCT, current,
|
||||||
|
arch_task_struct_size);
|
||||||
|
|
||||||
|
tsz = min_t(size_t, buflen, notes_offset + notes_len - *fpos);
|
||||||
|
if (copy_to_user(buffer, notes + *fpos - notes_offset, tsz)) {
|
||||||
|
kfree(notes);
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
kfree(notes);
|
||||||
|
|
||||||
|
buffer += tsz;
|
||||||
|
buflen -= tsz;
|
||||||
|
*fpos += tsz;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check to see if our file offset matches with any of
|
* Check to see if our file offset matches with any of
|
||||||
* the addresses in the elf_phdr on our list.
|
* the addresses in the elf_phdr on our list.
|
||||||
*/
|
*/
|
||||||
start = kc_offset_to_vaddr(*fpos - elf_buflen);
|
start = kc_offset_to_vaddr(*fpos - data_offset);
|
||||||
if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
|
if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
|
||||||
tsz = buflen;
|
tsz = buflen;
|
||||||
|
|
||||||
while (buflen) {
|
while (buflen) {
|
||||||
struct kcore_list *m;
|
|
||||||
|
|
||||||
list_for_each_entry(m, &kclist_head, list) {
|
list_for_each_entry(m, &kclist_head, list) {
|
||||||
if (start >= m->addr && start < (m->addr+m->size))
|
if (start >= m->addr && start < (m->addr+m->size))
|
||||||
break;
|
break;
|
||||||
|
@ -557,7 +490,6 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
|
||||||
return orig_buflen - buflen;
|
return orig_buflen - buflen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int open_kcore(struct inode *inode, struct file *filp)
|
static int open_kcore(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
if (!capable(CAP_SYS_RAWIO))
|
if (!capable(CAP_SYS_RAWIO))
|
||||||
|
|
Loading…
Reference in New Issue