Merge branch 'for-linus-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
Pull UML updates from Richard Weinberger: - remove hppfs ("HonePot ProcFS") - initial support for musl libc - uaccess cleanup - random cleanups and bug fixes all over the place * 'for-linus-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml: (21 commits) um: Don't pollute kernel namespace with uapi um: Include sys/types.h for makedev(), major(), minor() um: Do not use stdin and stdout identifiers for struct members um: Do not use __ptr_t type for stack_t's .ss pointer um: Fix mconsole dependency um: Handle tracehook_report_syscall_entry() result um: Remove copy&paste code from init.h um: Stop abusing __KERNEL__ um: Catch unprotected user memory access um: Fix warning in setup_signal_stack_si() um: Rework uaccess code um: Add uaccess.h to ldt.c um: Add uaccess.h to syscalls_64.c um: Add asm/elf.h to vma.c um: Cleanup mem_32/64.c headers um: Remove hppfs um: Move syscall() declaration into os.h um: kernel: ksyms: Export symbol syscall() for fixing modpost issue um/os-Linux: Use char[] for syscall_stub declarations um: Use char[] for linker script address declarations ...
This commit is contained in:
commit
21dc2e6c6d
|
@ -44,23 +44,9 @@ config HOSTFS
|
|||
If you'd like to be able to work with files stored on the host,
|
||||
say Y or M here; otherwise say N.
|
||||
|
||||
config HPPFS
|
||||
tristate "HoneyPot ProcFS"
|
||||
depends on PROC_FS
|
||||
help
|
||||
hppfs (HoneyPot ProcFS) is a filesystem which allows UML /proc
|
||||
entries to be overridden, removed, or fabricated from the host.
|
||||
Its purpose is to allow a UML to appear to be a physical machine
|
||||
by removing or changing anything in /proc which gives away the
|
||||
identity of a UML.
|
||||
|
||||
See <http://user-mode-linux.sf.net/old/hppfs.html> for more information.
|
||||
|
||||
You only need this if you are setting up a UML honeypot. Otherwise,
|
||||
it is safe to say 'N' here.
|
||||
|
||||
config MCONSOLE
|
||||
bool "Management console"
|
||||
depends on PROC_FS
|
||||
default y
|
||||
help
|
||||
The user mode linux management console is a low-level interface to
|
||||
|
|
|
@ -68,9 +68,10 @@ KBUILD_CFLAGS += $(CFLAGS) $(CFLAGS-y) -D__arch_um__ \
|
|||
|
||||
KBUILD_AFLAGS += $(ARCH_INCLUDE)
|
||||
|
||||
USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -D__KERNEL__,,\
|
||||
$(patsubst -I%,,$(KBUILD_CFLAGS)))) $(ARCH_INCLUDE) $(MODE_INCLUDE) \
|
||||
$(filter -I%,$(CFLAGS)) -D_FILE_OFFSET_BITS=64 -idirafter include
|
||||
USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -I%,,$(KBUILD_CFLAGS))) \
|
||||
$(ARCH_INCLUDE) $(MODE_INCLUDE) $(filter -I%,$(CFLAGS)) \
|
||||
-D_FILE_OFFSET_BITS=64 -idirafter include \
|
||||
-D__KERNEL__ -D__UM_HOST__
|
||||
|
||||
#This will adjust *FLAGS accordingly to the platform.
|
||||
include $(ARCH_DIR)/Makefile-os-$(OS)
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
#include <os.h>
|
||||
|
||||
struct dog_data {
|
||||
int stdin;
|
||||
int stdout;
|
||||
int stdin_fd;
|
||||
int stdout_fd;
|
||||
int close_me[2];
|
||||
};
|
||||
|
||||
|
@ -18,11 +18,11 @@ static void pre_exec(void *d)
|
|||
{
|
||||
struct dog_data *data = d;
|
||||
|
||||
dup2(data->stdin, 0);
|
||||
dup2(data->stdout, 1);
|
||||
dup2(data->stdout, 2);
|
||||
close(data->stdin);
|
||||
close(data->stdout);
|
||||
dup2(data->stdin_fd, 0);
|
||||
dup2(data->stdout_fd, 1);
|
||||
dup2(data->stdout_fd, 2);
|
||||
close(data->stdin_fd);
|
||||
close(data->stdout_fd);
|
||||
close(data->close_me[0]);
|
||||
close(data->close_me[1]);
|
||||
}
|
||||
|
@ -49,8 +49,8 @@ int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock)
|
|||
goto out_close_in;
|
||||
}
|
||||
|
||||
data.stdin = out_fds[0];
|
||||
data.stdout = in_fds[1];
|
||||
data.stdin_fd = out_fds[0];
|
||||
data.stdout_fd = in_fds[1];
|
||||
data.close_me[0] = out_fds[1];
|
||||
data.close_me[1] = in_fds[0];
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef __MCONSOLE_H__
|
||||
#define __MCONSOLE_H__
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#ifdef __UM_HOST__
|
||||
#include <stdint.h>
|
||||
#define u32 uint32_t
|
||||
#endif
|
||||
|
|
|
@ -166,7 +166,7 @@ int net_sendto(int fd, void *buf, int len, void *to, int sock_len)
|
|||
|
||||
struct change_pre_exec_data {
|
||||
int close_me;
|
||||
int stdout;
|
||||
int stdout_fd;
|
||||
};
|
||||
|
||||
static void change_pre_exec(void *arg)
|
||||
|
@ -174,7 +174,7 @@ static void change_pre_exec(void *arg)
|
|||
struct change_pre_exec_data *data = arg;
|
||||
|
||||
close(data->close_me);
|
||||
dup2(data->stdout, 1);
|
||||
dup2(data->stdout_fd, 1);
|
||||
}
|
||||
|
||||
static int change_tramp(char **argv, char *output, int output_len)
|
||||
|
@ -189,7 +189,7 @@ static int change_tramp(char **argv, char *output, int output_len)
|
|||
return err;
|
||||
}
|
||||
pe_data.close_me = fds[0];
|
||||
pe_data.stdout = fds[1];
|
||||
pe_data.stdout_fd = fds[1];
|
||||
pid = run_helper(change_pre_exec, &pe_data, argv);
|
||||
|
||||
if (pid > 0) /* Avoid hang as we won't get data in failure case. */
|
||||
|
|
|
@ -55,8 +55,8 @@ static int set_up_tty(int fd)
|
|||
}
|
||||
|
||||
struct slip_pre_exec_data {
|
||||
int stdin;
|
||||
int stdout;
|
||||
int stdin_fd;
|
||||
int stdout_fd;
|
||||
int close_me;
|
||||
};
|
||||
|
||||
|
@ -64,9 +64,9 @@ static void slip_pre_exec(void *arg)
|
|||
{
|
||||
struct slip_pre_exec_data *data = arg;
|
||||
|
||||
if (data->stdin >= 0)
|
||||
dup2(data->stdin, 0);
|
||||
dup2(data->stdout, 1);
|
||||
if (data->stdin_fd >= 0)
|
||||
dup2(data->stdin_fd, 0);
|
||||
dup2(data->stdout_fd, 1);
|
||||
if (data->close_me >= 0)
|
||||
close(data->close_me);
|
||||
}
|
||||
|
@ -85,8 +85,8 @@ static int slip_tramp(char **argv, int fd)
|
|||
}
|
||||
|
||||
err = 0;
|
||||
pe_data.stdin = fd;
|
||||
pe_data.stdout = fds[1];
|
||||
pe_data.stdin_fd = fd;
|
||||
pe_data.stdout_fd = fds[1];
|
||||
pe_data.close_me = fds[0];
|
||||
err = run_helper(slip_pre_exec, &pe_data, argv);
|
||||
if (err < 0)
|
||||
|
|
|
@ -20,18 +20,18 @@ static int slirp_user_init(void *data, void *dev)
|
|||
}
|
||||
|
||||
struct slirp_pre_exec_data {
|
||||
int stdin;
|
||||
int stdout;
|
||||
int stdin_fd;
|
||||
int stdout_fd;
|
||||
};
|
||||
|
||||
static void slirp_pre_exec(void *arg)
|
||||
{
|
||||
struct slirp_pre_exec_data *data = arg;
|
||||
|
||||
if (data->stdin != -1)
|
||||
dup2(data->stdin, 0);
|
||||
if (data->stdout != -1)
|
||||
dup2(data->stdout, 1);
|
||||
if (data->stdin_fd != -1)
|
||||
dup2(data->stdin_fd, 0);
|
||||
if (data->stdout_fd != -1)
|
||||
dup2(data->stdout_fd, 1);
|
||||
}
|
||||
|
||||
static int slirp_tramp(char **argv, int fd)
|
||||
|
@ -39,8 +39,8 @@ static int slirp_tramp(char **argv, int fd)
|
|||
struct slirp_pre_exec_data pe_data;
|
||||
int pid;
|
||||
|
||||
pe_data.stdin = fd;
|
||||
pe_data.stdout = fd;
|
||||
pe_data.stdin_fd = fd;
|
||||
pe_data.stdout_fd = fd;
|
||||
pid = run_helper(slirp_pre_exec, &pe_data, argv);
|
||||
|
||||
return pid;
|
||||
|
|
|
@ -21,7 +21,6 @@ generic-y += param.h
|
|||
generic-y += pci.h
|
||||
generic-y += percpu.h
|
||||
generic-y += preempt.h
|
||||
generic-y += sections.h
|
||||
generic-y += switch_to.h
|
||||
generic-y += topology.h
|
||||
generic-y += trace_clock.h
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <asm/ptrace-abi.h>
|
||||
#include <sysdep/ptrace.h>
|
||||
|
||||
struct pt_regs {
|
||||
|
@ -37,7 +36,7 @@ extern int putreg(struct task_struct *child, int regno, unsigned long value);
|
|||
|
||||
extern int arch_copy_tls(struct task_struct *new);
|
||||
extern void clear_flushed_tls(struct task_struct *task);
|
||||
extern void syscall_trace_enter(struct pt_regs *regs);
|
||||
extern int syscall_trace_enter(struct pt_regs *regs);
|
||||
extern void syscall_trace_leave(struct pt_regs *regs);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __UM_SECTIONS_H
|
||||
#define __UM_SECTIONS_H
|
||||
|
||||
#include <asm-generic/sections.h>
|
||||
|
||||
extern char __binary_start[];
|
||||
extern char __syscall_stub_start[], __syscall_stub_end[];
|
||||
|
||||
#endif
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include <asm/types.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/segment.h>
|
||||
|
||||
struct thread_info {
|
||||
struct task_struct *task; /* main task structure */
|
||||
|
|
|
@ -1,178 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Copyright (C) 2015 Richard Weinberger (richard@nod.at)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __UM_UACCESS_H
|
||||
#define __UM_UACCESS_H
|
||||
|
||||
/* thread_info has a mm_segment_t in it, so put the definition up here */
|
||||
typedef struct {
|
||||
unsigned long seg;
|
||||
} mm_segment_t;
|
||||
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/elf.h>
|
||||
|
||||
#define VERIFY_READ 0
|
||||
#define VERIFY_WRITE 1
|
||||
|
||||
/*
|
||||
* The fs value determines whether argument validity checking should be
|
||||
* performed or not. If get_fs() == USER_DS, checking is performed, with
|
||||
* get_fs() == KERNEL_DS, checking is bypassed.
|
||||
*
|
||||
* For historical reasons, these macros are grossly misnamed.
|
||||
*/
|
||||
|
||||
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
|
||||
|
||||
#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF)
|
||||
#define USER_DS MAKE_MM_SEG(TASK_SIZE)
|
||||
|
||||
#define get_ds() (KERNEL_DS)
|
||||
#define get_fs() (current_thread_info()->addr_limit)
|
||||
#define set_fs(x) (current_thread_info()->addr_limit = (x))
|
||||
|
||||
#define segment_eq(a, b) ((a).seg == (b).seg)
|
||||
|
||||
#define __under_task_size(addr, size) \
|
||||
(((unsigned long) (addr) < TASK_SIZE) && \
|
||||
(((unsigned long) (addr) + (size)) < TASK_SIZE))
|
||||
|
||||
#define __access_ok_vsyscall(type, addr, size) \
|
||||
((type == VERIFY_READ) && \
|
||||
((unsigned long) (addr) >= FIXADDR_USER_START) && \
|
||||
#define __access_ok_vsyscall(addr, size) \
|
||||
(((unsigned long) (addr) >= FIXADDR_USER_START) && \
|
||||
((unsigned long) (addr) + (size) <= FIXADDR_USER_END) && \
|
||||
((unsigned long) (addr) + (size) >= (unsigned long)(addr)))
|
||||
|
||||
#define __addr_range_nowrap(addr, size) \
|
||||
((unsigned long) (addr) <= ((unsigned long) (addr) + (size)))
|
||||
|
||||
#define access_ok(type, addr, size) \
|
||||
(__addr_range_nowrap(addr, size) && \
|
||||
(__under_task_size(addr, size) || \
|
||||
__access_ok_vsyscall(type, addr, size) || \
|
||||
segment_eq(get_fs(), KERNEL_DS)))
|
||||
|
||||
extern int copy_from_user(void *to, const void __user *from, int n);
|
||||
extern int copy_to_user(void __user *to, const void *from, int n);
|
||||
|
||||
/*
|
||||
* strncpy_from_user: - Copy a NUL terminated string from userspace.
|
||||
* @dst: Destination address, in kernel space. This buffer must be at
|
||||
* least @count bytes long.
|
||||
* @src: Source address, in user space.
|
||||
* @count: Maximum number of bytes to copy, including the trailing NUL.
|
||||
*
|
||||
* Copies a NUL-terminated string from userspace to kernel space.
|
||||
*
|
||||
* On success, returns the length of the string (not including the trailing
|
||||
* NUL).
|
||||
*
|
||||
* If access to userspace fails, returns -EFAULT (some data may have been
|
||||
* copied).
|
||||
*
|
||||
* If @count is smaller than the length of the string, copies @count bytes
|
||||
* and returns @count.
|
||||
*/
|
||||
|
||||
extern int strncpy_from_user(char *dst, const char __user *src, int count);
|
||||
|
||||
/*
|
||||
* __clear_user: - Zero a block of memory in user space, with less checking.
|
||||
* @to: Destination address, in user space.
|
||||
* @n: Number of bytes to zero.
|
||||
*
|
||||
* Zero a block of memory in user space. Caller must check
|
||||
* the specified block with access_ok() before calling this function.
|
||||
*
|
||||
* Returns number of bytes that could not be cleared.
|
||||
* On success, this will be zero.
|
||||
*/
|
||||
extern int __clear_user(void __user *mem, int len);
|
||||
|
||||
/*
|
||||
* clear_user: - Zero a block of memory in user space.
|
||||
* @to: Destination address, in user space.
|
||||
* @n: Number of bytes to zero.
|
||||
*
|
||||
* Zero a block of memory in user space.
|
||||
*
|
||||
* Returns number of bytes that could not be cleared.
|
||||
* On success, this will be zero.
|
||||
*/
|
||||
extern int clear_user(void __user *mem, int len);
|
||||
|
||||
/*
|
||||
* strlen_user: - Get the size of a string in user space.
|
||||
* @str: The string to measure.
|
||||
* @n: The maximum valid length
|
||||
*
|
||||
* Get the size of a NUL-terminated string in user space.
|
||||
*
|
||||
* Returns the size of the string INCLUDING the terminating NUL.
|
||||
* On exception, returns 0.
|
||||
* If the string is too long, returns a value greater than @n.
|
||||
*/
|
||||
extern int strnlen_user(const void __user *str, int len);
|
||||
|
||||
#define __copy_from_user(to, from, n) copy_from_user(to, from, n)
|
||||
|
||||
#define __copy_to_user(to, from, n) copy_to_user(to, from, n)
|
||||
extern long __copy_from_user(void *to, const void __user *from, unsigned long n);
|
||||
extern long __copy_to_user(void __user *to, const void *from, unsigned long n);
|
||||
extern long __strncpy_from_user(char *dst, const char __user *src, long count);
|
||||
extern long __strnlen_user(const void __user *str, long len);
|
||||
extern unsigned long __clear_user(void __user *mem, unsigned long len);
|
||||
static inline int __access_ok(unsigned long addr, unsigned long size);
|
||||
|
||||
/* Teach asm-generic/uaccess.h that we have C functions for these. */
|
||||
#define __access_ok __access_ok
|
||||
#define __clear_user __clear_user
|
||||
#define __copy_to_user __copy_to_user
|
||||
#define __copy_from_user __copy_from_user
|
||||
#define __strnlen_user __strnlen_user
|
||||
#define __strncpy_from_user __strncpy_from_user
|
||||
#define __copy_to_user_inatomic __copy_to_user
|
||||
#define __copy_from_user_inatomic __copy_from_user
|
||||
|
||||
#define __get_user(x, ptr) \
|
||||
({ \
|
||||
const __typeof__(*(ptr)) __user *__private_ptr = (ptr); \
|
||||
__typeof__(x) __private_val; \
|
||||
int __private_ret = -EFAULT; \
|
||||
(x) = (__typeof__(*(__private_ptr)))0; \
|
||||
if (__copy_from_user((__force void *)&__private_val, (__private_ptr),\
|
||||
sizeof(*(__private_ptr))) == 0) { \
|
||||
(x) = (__typeof__(*(__private_ptr))) __private_val; \
|
||||
__private_ret = 0; \
|
||||
} \
|
||||
__private_ret; \
|
||||
})
|
||||
#include <asm-generic/uaccess.h>
|
||||
|
||||
#define get_user(x, ptr) \
|
||||
({ \
|
||||
const __typeof__((*(ptr))) __user *private_ptr = (ptr); \
|
||||
(access_ok(VERIFY_READ, private_ptr, sizeof(*private_ptr)) ? \
|
||||
__get_user(x, private_ptr) : ((x) = (__typeof__(*ptr))0, -EFAULT)); \
|
||||
})
|
||||
|
||||
#define __put_user(x, ptr) \
|
||||
({ \
|
||||
__typeof__(*(ptr)) __user *__private_ptr = ptr; \
|
||||
__typeof__(*(__private_ptr)) __private_val; \
|
||||
int __private_ret = -EFAULT; \
|
||||
__private_val = (__typeof__(*(__private_ptr))) (x); \
|
||||
if (__copy_to_user((__private_ptr), &__private_val, \
|
||||
sizeof(*(__private_ptr))) == 0) { \
|
||||
__private_ret = 0; \
|
||||
} \
|
||||
__private_ret; \
|
||||
})
|
||||
|
||||
#define put_user(x, ptr) \
|
||||
({ \
|
||||
__typeof__(*(ptr)) __user *private_ptr = (ptr); \
|
||||
(access_ok(VERIFY_WRITE, private_ptr, sizeof(*private_ptr)) ? \
|
||||
__put_user(x, private_ptr) : -EFAULT); \
|
||||
})
|
||||
|
||||
#define strlen_user(str) strnlen_user(str, ~0U >> 1)
|
||||
|
||||
struct exception_table_entry
|
||||
static inline int __access_ok(unsigned long addr, unsigned long size)
|
||||
{
|
||||
unsigned long insn;
|
||||
unsigned long fixup;
|
||||
};
|
||||
return __addr_range_nowrap(addr, size) &&
|
||||
(__under_task_size(addr, size) ||
|
||||
__access_ok_vsyscall(addr, size) ||
|
||||
segment_eq(get_fs(), KERNEL_DS));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,28 +40,8 @@
|
|||
typedef int (*initcall_t)(void);
|
||||
typedef void (*exitcall_t)(void);
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#ifndef __section
|
||||
# define __section(S) __attribute__ ((__section__(#S)))
|
||||
#endif
|
||||
|
||||
#if __GNUC__ == 3
|
||||
|
||||
#if __GNUC_MINOR__ >= 3
|
||||
# define __used __attribute__((__used__))
|
||||
#else
|
||||
# define __used __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if __GNUC__ == 4
|
||||
# define __used __attribute__((__used__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <linux/compiler.h>
|
||||
#endif
|
||||
|
||||
/* These are for everybody (although not all archs will actually
|
||||
discard it in modules) */
|
||||
#define __init __section(.init.text)
|
||||
|
@ -131,7 +111,7 @@ extern struct uml_param __uml_setup_start, __uml_setup_end;
|
|||
#define __uml_postsetup_call __used __section(.uml.postsetup.init)
|
||||
#define __uml_exit_call __used __section(.uml.exitcall.exit)
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#ifdef __UM_HOST__
|
||||
|
||||
#define __define_initcall(level,fn) \
|
||||
static initcall_t __initcall_##fn __used \
|
||||
|
|
|
@ -301,4 +301,6 @@ extern int get_pty(void);
|
|||
/* sys-$ARCH/task_size.c */
|
||||
extern unsigned long os_get_top_address(void);
|
||||
|
||||
long syscall(long number, ...);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
/* This is to get size_t */
|
||||
#ifdef __KERNEL__
|
||||
#ifndef __UM_HOST__
|
||||
#include <linux/types.h>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
|
|
|
@ -42,3 +42,5 @@ EXPORT_SYMBOL(os_makedev);
|
|||
EXPORT_SYMBOL(add_sigio_fd);
|
||||
EXPORT_SYMBOL(ignore_sigio_fd);
|
||||
EXPORT_SYMBOL(sigio_broken);
|
||||
|
||||
EXPORT_SYMBOL(syscall);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
#include <as-layout.h>
|
||||
#include <init.h>
|
||||
#include <kern.h>
|
||||
|
@ -55,8 +56,6 @@ void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
|
|||
}
|
||||
}
|
||||
|
||||
extern int __syscall_stub_start;
|
||||
|
||||
/**
|
||||
* setup_physmem() - Setup physical memory for UML
|
||||
* @start: Start address of the physical kernel memory,
|
||||
|
@ -110,8 +109,8 @@ void __init setup_physmem(unsigned long start, unsigned long reserve_end,
|
|||
* Special kludge - This page will be mapped in to userspace processes
|
||||
* from physmem_fd, so it needs to be written out there.
|
||||
*/
|
||||
os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
|
||||
os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
|
||||
os_seek_file(physmem_fd, __pa(__syscall_stub_start));
|
||||
os_write_file(physmem_fd, __syscall_stub_start, PAGE_SIZE);
|
||||
os_fsync_file(physmem_fd);
|
||||
|
||||
bootmap_size = init_bootmem(pfn, pfn + delta);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/tracehook.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ptrace-abi.h>
|
||||
|
||||
void user_enable_single_step(struct task_struct *child)
|
||||
{
|
||||
|
@ -131,7 +132,7 @@ static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs,
|
|||
* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
|
||||
* PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
|
||||
*/
|
||||
void syscall_trace_enter(struct pt_regs *regs)
|
||||
int syscall_trace_enter(struct pt_regs *regs)
|
||||
{
|
||||
audit_syscall_entry(UPT_SYSCALL_NR(®s->regs),
|
||||
UPT_SYSCALL_ARG1(®s->regs),
|
||||
|
@ -140,9 +141,9 @@ void syscall_trace_enter(struct pt_regs *regs)
|
|||
UPT_SYSCALL_ARG4(®s->regs));
|
||||
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
tracehook_report_syscall_entry(regs);
|
||||
return tracehook_report_syscall_entry(regs);
|
||||
}
|
||||
|
||||
void syscall_trace_leave(struct pt_regs *regs)
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
#include <linux/slab.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/sections.h>
|
||||
#include <as-layout.h>
|
||||
#include <os.h>
|
||||
#include <skas.h>
|
||||
|
||||
extern int __syscall_stub_start;
|
||||
|
||||
static int init_stub_pte(struct mm_struct *mm, unsigned long proc,
|
||||
unsigned long kernel)
|
||||
{
|
||||
|
@ -93,7 +92,7 @@ void uml_setup_stubs(struct mm_struct *mm)
|
|||
int err, ret;
|
||||
|
||||
ret = init_stub_pte(mm, STUB_CODE,
|
||||
(unsigned long) &__syscall_stub_start);
|
||||
(unsigned long) __syscall_stub_start);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -101,7 +100,7 @@ void uml_setup_stubs(struct mm_struct *mm)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
mm->context.stub_pages[0] = virt_to_page(&__syscall_stub_start);
|
||||
mm->context.stub_pages[0] = virt_to_page(__syscall_stub_start);
|
||||
mm->context.stub_pages[1] = virt_to_page(mm->context.id.stack);
|
||||
|
||||
/* dup_mmap already holds mmap_sem */
|
||||
|
|
|
@ -18,7 +18,10 @@ void handle_syscall(struct uml_pt_regs *r)
|
|||
long result;
|
||||
int syscall;
|
||||
|
||||
syscall_trace_enter(regs);
|
||||
if (syscall_trace_enter(regs)) {
|
||||
result = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* This should go in the declaration of syscall, but when I do that,
|
||||
|
@ -34,6 +37,7 @@ void handle_syscall(struct uml_pt_regs *r)
|
|||
result = -ENOSYS;
|
||||
else result = EXECUTE_SYSCALL(syscall, regs);
|
||||
|
||||
out:
|
||||
PT_REGS_SET_SYSCALL_RETURN(regs, result);
|
||||
|
||||
syscall_trace_leave(regs);
|
||||
|
|
|
@ -87,10 +87,10 @@ static int do_op_one_page(unsigned long addr, int len, int is_write,
|
|||
return n;
|
||||
}
|
||||
|
||||
static int buffer_op(unsigned long addr, int len, int is_write,
|
||||
static long buffer_op(unsigned long addr, int len, int is_write,
|
||||
int (*op)(unsigned long, int, void *), void *arg)
|
||||
{
|
||||
int size, remain, n;
|
||||
long size, remain, n;
|
||||
|
||||
size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
|
||||
remain = len;
|
||||
|
@ -139,18 +139,16 @@ static int copy_chunk_from_user(unsigned long from, int len, void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int copy_from_user(void *to, const void __user *from, int n)
|
||||
long __copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||
{
|
||||
if (segment_eq(get_fs(), KERNEL_DS)) {
|
||||
memcpy(to, (__force void*)from, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return access_ok(VERIFY_READ, from, n) ?
|
||||
buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
|
||||
n;
|
||||
return buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to);
|
||||
}
|
||||
EXPORT_SYMBOL(copy_from_user);
|
||||
EXPORT_SYMBOL(__copy_from_user);
|
||||
|
||||
static int copy_chunk_to_user(unsigned long to, int len, void *arg)
|
||||
{
|
||||
|
@ -161,18 +159,16 @@ static int copy_chunk_to_user(unsigned long to, int len, void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int copy_to_user(void __user *to, const void *from, int n)
|
||||
long __copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
if (segment_eq(get_fs(), KERNEL_DS)) {
|
||||
memcpy((__force void *) to, from, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return access_ok(VERIFY_WRITE, to, n) ?
|
||||
buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
|
||||
n;
|
||||
return buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from);
|
||||
}
|
||||
EXPORT_SYMBOL(copy_to_user);
|
||||
EXPORT_SYMBOL(__copy_to_user);
|
||||
|
||||
static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
|
||||
{
|
||||
|
@ -188,9 +184,9 @@ static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int strncpy_from_user(char *dst, const char __user *src, int count)
|
||||
long __strncpy_from_user(char *dst, const char __user *src, long count)
|
||||
{
|
||||
int n;
|
||||
long n;
|
||||
char *ptr = dst;
|
||||
|
||||
if (segment_eq(get_fs(), KERNEL_DS)) {
|
||||
|
@ -198,16 +194,13 @@ int strncpy_from_user(char *dst, const char __user *src, int count)
|
|||
return strnlen(dst, count);
|
||||
}
|
||||
|
||||
if (!access_ok(VERIFY_READ, src, 1))
|
||||
return -EFAULT;
|
||||
|
||||
n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
|
||||
&ptr);
|
||||
if (n != 0)
|
||||
return -EFAULT;
|
||||
return strnlen(dst, count);
|
||||
}
|
||||
EXPORT_SYMBOL(strncpy_from_user);
|
||||
EXPORT_SYMBOL(__strncpy_from_user);
|
||||
|
||||
static int clear_chunk(unsigned long addr, int len, void *unused)
|
||||
{
|
||||
|
@ -215,22 +208,16 @@ static int clear_chunk(unsigned long addr, int len, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int __clear_user(void __user *mem, int len)
|
||||
{
|
||||
return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
|
||||
}
|
||||
|
||||
int clear_user(void __user *mem, int len)
|
||||
unsigned long __clear_user(void __user *mem, unsigned long len)
|
||||
{
|
||||
if (segment_eq(get_fs(), KERNEL_DS)) {
|
||||
memset((__force void*)mem, 0, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return access_ok(VERIFY_WRITE, mem, len) ?
|
||||
buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len;
|
||||
return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(clear_user);
|
||||
EXPORT_SYMBOL(__clear_user);
|
||||
|
||||
static int strnlen_chunk(unsigned long str, int len, void *arg)
|
||||
{
|
||||
|
@ -244,7 +231,7 @@ static int strnlen_chunk(unsigned long str, int len, void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int strnlen_user(const void __user *str, int len)
|
||||
long __strnlen_user(const void __user *str, long len)
|
||||
{
|
||||
int count = 0, n;
|
||||
|
||||
|
@ -256,4 +243,4 @@ int strnlen_user(const void __user *str, int len)
|
|||
return count + 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(strnlen_user);
|
||||
EXPORT_SYMBOL(__strnlen_user);
|
||||
|
|
|
@ -220,6 +220,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
|||
show_regs(container_of(regs, struct pt_regs, regs));
|
||||
panic("Segfault with no mm");
|
||||
}
|
||||
else if (!is_user && address < TASK_SIZE) {
|
||||
show_regs(container_of(regs, struct pt_regs, regs));
|
||||
panic("Kernel tried to access user memory at addr 0x%lx, ip 0x%lx",
|
||||
address, ip);
|
||||
}
|
||||
|
||||
if (SEGV_IS_FIXABLE(&fi))
|
||||
err = handle_page_fault(address, ip, is_write, is_user,
|
||||
|
|
|
@ -248,8 +248,6 @@ EXPORT_SYMBOL(end_iomem);
|
|||
|
||||
#define MIN_VMALLOC (32 * 1024 * 1024)
|
||||
|
||||
extern char __binary_start;
|
||||
|
||||
int __init linux_main(int argc, char **argv)
|
||||
{
|
||||
unsigned long avail, diff;
|
||||
|
@ -294,7 +292,7 @@ int __init linux_main(int argc, char **argv)
|
|||
physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
|
||||
}
|
||||
|
||||
uml_physmem = (unsigned long) &__binary_start & PAGE_MASK;
|
||||
uml_physmem = (unsigned long) __binary_start & PAGE_MASK;
|
||||
|
||||
/* Reserve up to 4M after the current brk */
|
||||
uml_reserved = ROUND_4M(brk_start) + (1 << 22);
|
||||
|
|
|
@ -47,7 +47,7 @@ static void tuntap_del_addr(unsigned char *addr, unsigned char *netmask,
|
|||
}
|
||||
|
||||
struct tuntap_pre_exec_data {
|
||||
int stdout;
|
||||
int stdout_fd;
|
||||
int close_me;
|
||||
};
|
||||
|
||||
|
@ -55,7 +55,7 @@ static void tuntap_pre_exec(void *arg)
|
|||
{
|
||||
struct tuntap_pre_exec_data *data = arg;
|
||||
|
||||
dup2(data->stdout, 1);
|
||||
dup2(data->stdout_fd, 1);
|
||||
close(data->close_me);
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ static int tuntap_open_tramp(char *gate, int *fd_out, int me, int remote,
|
|||
|
||||
sprintf(version_buf, "%d", UML_NET_VERSION);
|
||||
|
||||
data.stdout = remote;
|
||||
data.stdout_fd = remote;
|
||||
data.close_me = me;
|
||||
|
||||
pid = run_helper(tuntap_pre_exec, &data, argv);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
#include <os.h>
|
||||
|
||||
static void copy_stat(struct uml_stat *dst, const struct stat64 *src)
|
||||
|
|
|
@ -112,9 +112,11 @@ void timer_init(void)
|
|||
|
||||
void set_sigstack(void *sig_stack, int size)
|
||||
{
|
||||
stack_t stack = ((stack_t) { .ss_flags = 0,
|
||||
.ss_sp = (__ptr_t) sig_stack,
|
||||
.ss_size = size - sizeof(void *) });
|
||||
stack_t stack = {
|
||||
.ss_flags = 0,
|
||||
.ss_sp = sig_stack,
|
||||
.ss_size = size - sizeof(void *)
|
||||
};
|
||||
|
||||
if (sigaltstack(&stack, NULL) != 0)
|
||||
panic("enabling signal stack failed, errno = %d\n", errno);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <sysdep/ptrace.h>
|
||||
#include <sysdep/stub.h>
|
||||
|
||||
extern unsigned long batch_syscall_stub, __syscall_stub_start;
|
||||
extern char batch_syscall_stub[], __syscall_stub_start[];
|
||||
|
||||
extern void wait_stub_done(int pid);
|
||||
|
||||
|
@ -38,8 +38,8 @@ static int __init init_syscall_regs(void)
|
|||
{
|
||||
get_safe_registers(syscall_regs, NULL);
|
||||
syscall_regs[REGS_IP_INDEX] = STUB_CODE +
|
||||
((unsigned long) &batch_syscall_stub -
|
||||
(unsigned long) &__syscall_stub_start);
|
||||
((unsigned long) batch_syscall_stub -
|
||||
(unsigned long) __syscall_stub_start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ static void handle_trap(int pid, struct uml_pt_regs *regs,
|
|||
handle_syscall(regs);
|
||||
}
|
||||
|
||||
extern int __syscall_stub_start;
|
||||
extern char __syscall_stub_start[];
|
||||
|
||||
static int userspace_tramp(void *stack)
|
||||
{
|
||||
|
@ -197,7 +197,7 @@ static int userspace_tramp(void *stack)
|
|||
* This has a pte, but it can't be mapped in with the usual
|
||||
* tlb_flush mechanism because this is part of that mechanism
|
||||
*/
|
||||
fd = phys_mapping(to_phys(&__syscall_stub_start), &offset);
|
||||
fd = phys_mapping(to_phys(__syscall_stub_start), &offset);
|
||||
addr = mmap64((void *) STUB_CODE, UM_KERN_PAGE_SIZE,
|
||||
PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, offset);
|
||||
if (addr == MAP_FAILED) {
|
||||
|
@ -223,7 +223,7 @@ static int userspace_tramp(void *stack)
|
|||
|
||||
unsigned long v = STUB_CODE +
|
||||
(unsigned long) stub_segv_handler -
|
||||
(unsigned long) &__syscall_stub_start;
|
||||
(unsigned long) __syscall_stub_start;
|
||||
|
||||
set_sigstack((void *) STUB_DATA, UM_KERN_PAGE_SIZE);
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
@ -447,7 +447,7 @@ static int __init init_thread_regs(void)
|
|||
/* Set parent's instruction pointer to start of clone-stub */
|
||||
thread_regs[REGS_IP_INDEX] = STUB_CODE +
|
||||
(unsigned long) stub_clone_handler -
|
||||
(unsigned long) &__syscall_stub_start;
|
||||
(unsigned long) __syscall_stub_start;
|
||||
thread_regs[REGS_SP_INDEX] = STUB_DATA + UM_KERN_PAGE_SIZE -
|
||||
sizeof(void *);
|
||||
#ifdef __SIGNAL_FRAMESIZE
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <linux/string.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/*
|
||||
* computes the checksum of a memory block at buff, length len,
|
||||
|
|
|
@ -200,8 +200,6 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG];
|
|||
|
||||
typedef struct user_i387_struct elf_fpregset_t;
|
||||
|
||||
#define task_pt_regs(t) (&(t)->thread.regs)
|
||||
|
||||
struct task_struct;
|
||||
|
||||
extern int elf_core_copy_fpregs(struct task_struct *t, elf_fpregset_t *fpu);
|
||||
|
|
|
@ -28,6 +28,8 @@ static inline void rep_nop(void)
|
|||
#define cpu_relax() rep_nop()
|
||||
#define cpu_relax_lowlatency() cpu_relax()
|
||||
|
||||
#define task_pt_regs(t) (&(t)->thread.regs)
|
||||
|
||||
#include <asm/processor-generic.h>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,4 +7,12 @@ extern int host_gdt_entry_tls_min;
|
|||
#define GDT_ENTRY_TLS_MIN host_gdt_entry_tls_min
|
||||
#define GDT_ENTRY_TLS_MAX (GDT_ENTRY_TLS_MIN + GDT_ENTRY_TLS_ENTRIES - 1)
|
||||
|
||||
typedef struct {
|
||||
unsigned long seg;
|
||||
} mm_segment_t;
|
||||
|
||||
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
|
||||
#define KERNEL_DS MAKE_MM_SEG(~0UL)
|
||||
#define USER_DS MAKE_MM_SEG(TASK_SIZE)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <os.h>
|
||||
#include <skas.h>
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/mman.h>
|
||||
#include <asm/elf.h>
|
||||
|
||||
static struct vm_area_struct gate_vma;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include <linux/mm.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/mman.h>
|
||||
#include <asm/elf.h>
|
||||
|
||||
const char *arch_vma_name(struct vm_area_struct *vma)
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ptrace-abi.h>
|
||||
#include <skas.h>
|
||||
|
||||
extern int arch_switch_tls(struct task_struct *to);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define __FRAME_OFFSETS
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ptrace-abi.h>
|
||||
|
||||
/*
|
||||
* determines which flags the user has access to.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef _SYSDEP_TLS_H
|
||||
#define _SYSDEP_TLS_H
|
||||
|
||||
# ifndef __KERNEL__
|
||||
#ifdef __UM_HOST__
|
||||
|
||||
/* Change name to avoid conflicts with the original one from <asm/ldt.h>, which
|
||||
* may be named user_desc (but in 2.4 and in header matching its API was named
|
||||
|
@ -22,11 +22,11 @@ typedef struct um_dup_user_desc {
|
|||
#endif
|
||||
} user_desc_t;
|
||||
|
||||
# else /* __KERNEL__ */
|
||||
#else /* __UM_HOST__ */
|
||||
|
||||
typedef struct user_desc user_desc_t;
|
||||
|
||||
# endif /* __KERNEL__ */
|
||||
#endif /* __UM_HOST__ */
|
||||
|
||||
extern int os_set_thread_area(user_desc_t *info, int pid);
|
||||
extern int os_get_thread_area(user_desc_t *info, int pid);
|
||||
|
|
|
@ -541,7 +541,8 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig,
|
|||
*/
|
||||
/* x86-64 should always use SA_RESTORER. */
|
||||
if (ksig->ka.sa.sa_flags & SA_RESTORER)
|
||||
err |= __put_user(ksig->ka.sa.sa_restorer, &frame->pretcode);
|
||||
err |= __put_user((void *)ksig->ka.sa.sa_restorer,
|
||||
&frame->pretcode);
|
||||
else
|
||||
/* could use a vstub here */
|
||||
return err;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/prctl.h> /* XXX This should get the constants from libc */
|
||||
#include <os.h>
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ptrace-abi.h>
|
||||
#include <os.h>
|
||||
#include <skas.h>
|
||||
#include <sysdep/tls.h>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <linux/sched.h>
|
||||
#include <asm/ptrace-abi.h>
|
||||
|
||||
void clear_flushed_tls(struct task_struct *task)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/elf.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static unsigned int __read_mostly vdso_enabled = 1;
|
||||
|
|
|
@ -115,7 +115,6 @@ obj-$(CONFIG_AFS_FS) += afs/
|
|||
obj-$(CONFIG_NILFS2_FS) += nilfs2/
|
||||
obj-$(CONFIG_BEFS_FS) += befs/
|
||||
obj-$(CONFIG_HOSTFS) += hostfs/
|
||||
obj-$(CONFIG_HPPFS) += hppfs/
|
||||
obj-$(CONFIG_CACHEFILES) += cachefiles/
|
||||
obj-$(CONFIG_DEBUG_FS) += debugfs/
|
||||
obj-$(CONFIG_TRACING) += tracefs/
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HPPFS) += hppfs.o
|
765
fs/hppfs/hppfs.c
765
fs/hppfs/hppfs.c
|
@ -1,765 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/namei.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <os.h>
|
||||
|
||||
static struct inode *get_inode(struct super_block *, struct dentry *);
|
||||
|
||||
struct hppfs_data {
|
||||
struct list_head list;
|
||||
char contents[PAGE_SIZE - sizeof(struct list_head)];
|
||||
};
|
||||
|
||||
struct hppfs_private {
|
||||
struct file *proc_file;
|
||||
int host_fd;
|
||||
loff_t len;
|
||||
struct hppfs_data *contents;
|
||||
};
|
||||
|
||||
struct hppfs_inode_info {
|
||||
struct dentry *proc_dentry;
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
static inline struct hppfs_inode_info *HPPFS_I(struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct hppfs_inode_info, vfs_inode);
|
||||
}
|
||||
|
||||
#define HPPFS_SUPER_MAGIC 0xb00000ee
|
||||
|
||||
static const struct super_operations hppfs_sbops;
|
||||
|
||||
static int is_pid(struct dentry *dentry)
|
||||
{
|
||||
struct super_block *sb;
|
||||
int i;
|
||||
|
||||
sb = dentry->d_sb;
|
||||
if (dentry->d_parent != sb->s_root)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < dentry->d_name.len; i++) {
|
||||
if (!isdigit(dentry->d_name.name[i]))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *dentry_name(struct dentry *dentry, int extra)
|
||||
{
|
||||
struct dentry *parent;
|
||||
char *root, *name;
|
||||
const char *seg_name;
|
||||
int len, seg_len, root_len;
|
||||
|
||||
len = 0;
|
||||
parent = dentry;
|
||||
while (parent->d_parent != parent) {
|
||||
if (is_pid(parent))
|
||||
len += strlen("pid") + 1;
|
||||
else len += parent->d_name.len + 1;
|
||||
parent = parent->d_parent;
|
||||
}
|
||||
|
||||
root = "proc";
|
||||
root_len = strlen(root);
|
||||
len += root_len;
|
||||
name = kmalloc(len + extra + 1, GFP_KERNEL);
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
name[len] = '\0';
|
||||
parent = dentry;
|
||||
while (parent->d_parent != parent) {
|
||||
if (is_pid(parent)) {
|
||||
seg_name = "pid";
|
||||
seg_len = strlen(seg_name);
|
||||
}
|
||||
else {
|
||||
seg_name = parent->d_name.name;
|
||||
seg_len = parent->d_name.len;
|
||||
}
|
||||
|
||||
len -= seg_len + 1;
|
||||
name[len] = '/';
|
||||
memcpy(&name[len + 1], seg_name, seg_len);
|
||||
parent = parent->d_parent;
|
||||
}
|
||||
memcpy(name, root, root_len);
|
||||
return name;
|
||||
}
|
||||
|
||||
static int file_removed(struct dentry *dentry, const char *file)
|
||||
{
|
||||
char *host_file;
|
||||
int extra, fd;
|
||||
|
||||
extra = 0;
|
||||
if (file != NULL)
|
||||
extra += strlen(file) + 1;
|
||||
|
||||
host_file = dentry_name(dentry, extra + strlen("/remove"));
|
||||
if (host_file == NULL) {
|
||||
printk(KERN_ERR "file_removed : allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (file != NULL) {
|
||||
strcat(host_file, "/");
|
||||
strcat(host_file, file);
|
||||
}
|
||||
strcat(host_file, "/remove");
|
||||
|
||||
fd = os_open_file(host_file, of_read(OPENFLAGS()), 0);
|
||||
kfree(host_file);
|
||||
if (fd > 0) {
|
||||
os_close_file(fd);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct dentry *proc_dentry, *parent;
|
||||
struct qstr *name = &dentry->d_name;
|
||||
struct inode *inode;
|
||||
int err, deleted;
|
||||
|
||||
deleted = file_removed(dentry, NULL);
|
||||
if (deleted < 0)
|
||||
return ERR_PTR(deleted);
|
||||
else if (deleted)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
parent = HPPFS_I(ino)->proc_dentry;
|
||||
mutex_lock(&d_inode(parent)->i_mutex);
|
||||
proc_dentry = lookup_one_len(name->name, parent, name->len);
|
||||
mutex_unlock(&d_inode(parent)->i_mutex);
|
||||
|
||||
if (IS_ERR(proc_dentry))
|
||||
return proc_dentry;
|
||||
|
||||
err = -ENOMEM;
|
||||
inode = get_inode(ino->i_sb, proc_dentry);
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static const struct inode_operations hppfs_file_iops = {
|
||||
};
|
||||
|
||||
static ssize_t read_proc(struct file *file, char __user *buf, ssize_t count,
|
||||
loff_t *ppos, int is_user)
|
||||
{
|
||||
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
|
||||
ssize_t n;
|
||||
|
||||
read = file_inode(file)->i_fop->read;
|
||||
|
||||
if (!is_user)
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
n = (*read)(file, buf, count, &file->f_pos);
|
||||
|
||||
if (!is_user)
|
||||
set_fs(USER_DS);
|
||||
|
||||
if (ppos)
|
||||
*ppos = file->f_pos;
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t hppfs_read_file(int fd, char __user *buf, ssize_t count)
|
||||
{
|
||||
ssize_t n;
|
||||
int cur, err;
|
||||
char *new_buf;
|
||||
|
||||
n = -ENOMEM;
|
||||
new_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (new_buf == NULL) {
|
||||
printk(KERN_ERR "hppfs_read_file : kmalloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
n = 0;
|
||||
while (count > 0) {
|
||||
cur = min_t(ssize_t, count, PAGE_SIZE);
|
||||
err = os_read_file(fd, new_buf, cur);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "hppfs_read : read failed, "
|
||||
"errno = %d\n", err);
|
||||
n = err;
|
||||
goto out_free;
|
||||
} else if (err == 0)
|
||||
break;
|
||||
|
||||
if (copy_to_user(buf, new_buf, err)) {
|
||||
n = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
n += err;
|
||||
count -= err;
|
||||
}
|
||||
out_free:
|
||||
kfree(new_buf);
|
||||
out:
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t hppfs_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct hppfs_private *hppfs = file->private_data;
|
||||
struct hppfs_data *data;
|
||||
loff_t off;
|
||||
int err;
|
||||
|
||||
if (hppfs->contents != NULL) {
|
||||
int rem;
|
||||
|
||||
if (*ppos >= hppfs->len)
|
||||
return 0;
|
||||
|
||||
data = hppfs->contents;
|
||||
off = *ppos;
|
||||
while (off >= sizeof(data->contents)) {
|
||||
data = list_entry(data->list.next, struct hppfs_data,
|
||||
list);
|
||||
off -= sizeof(data->contents);
|
||||
}
|
||||
|
||||
if (off + count > hppfs->len)
|
||||
count = hppfs->len - off;
|
||||
rem = copy_to_user(buf, &data->contents[off], count);
|
||||
*ppos += count - rem;
|
||||
if (rem > 0)
|
||||
return -EFAULT;
|
||||
} else if (hppfs->host_fd != -1) {
|
||||
err = os_seek_file(hppfs->host_fd, *ppos);
|
||||
if (err) {
|
||||
printk(KERN_ERR "hppfs_read : seek failed, "
|
||||
"errno = %d\n", err);
|
||||
return err;
|
||||
}
|
||||
err = hppfs_read_file(hppfs->host_fd, buf, count);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "hppfs_read: read failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
count = err;
|
||||
if (count > 0)
|
||||
*ppos += count;
|
||||
}
|
||||
else count = read_proc(hppfs->proc_file, buf, count, ppos, 1);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t hppfs_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
struct hppfs_private *data = file->private_data;
|
||||
struct file *proc_file = data->proc_file;
|
||||
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
|
||||
|
||||
write = file_inode(proc_file)->i_fop->write;
|
||||
return (*write)(proc_file, buf, len, ppos);
|
||||
}
|
||||
|
||||
static int open_host_sock(char *host_file, int *filter_out)
|
||||
{
|
||||
char *end;
|
||||
int fd;
|
||||
|
||||
end = &host_file[strlen(host_file)];
|
||||
strcpy(end, "/rw");
|
||||
*filter_out = 1;
|
||||
fd = os_connect_socket(host_file);
|
||||
if (fd > 0)
|
||||
return fd;
|
||||
|
||||
strcpy(end, "/r");
|
||||
*filter_out = 0;
|
||||
fd = os_connect_socket(host_file);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void free_contents(struct hppfs_data *head)
|
||||
{
|
||||
struct hppfs_data *data;
|
||||
struct list_head *ele, *next;
|
||||
|
||||
if (head == NULL)
|
||||
return;
|
||||
|
||||
list_for_each_safe(ele, next, &head->list) {
|
||||
data = list_entry(ele, struct hppfs_data, list);
|
||||
kfree(data);
|
||||
}
|
||||
kfree(head);
|
||||
}
|
||||
|
||||
static struct hppfs_data *hppfs_get_data(int fd, int filter,
|
||||
struct file *proc_file,
|
||||
struct file *hppfs_file,
|
||||
loff_t *size_out)
|
||||
{
|
||||
struct hppfs_data *data, *new, *head;
|
||||
int n, err;
|
||||
|
||||
err = -ENOMEM;
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
printk(KERN_ERR "hppfs_get_data : head allocation failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&data->list);
|
||||
|
||||
head = data;
|
||||
*size_out = 0;
|
||||
|
||||
if (filter) {
|
||||
while ((n = read_proc(proc_file, data->contents,
|
||||
sizeof(data->contents), NULL, 0)) > 0)
|
||||
os_write_file(fd, data->contents, n);
|
||||
err = os_shutdown_socket(fd, 0, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR "hppfs_get_data : failed to shut down "
|
||||
"socket\n");
|
||||
goto failed_free;
|
||||
}
|
||||
}
|
||||
while (1) {
|
||||
n = os_read_file(fd, data->contents, sizeof(data->contents));
|
||||
if (n < 0) {
|
||||
err = n;
|
||||
printk(KERN_ERR "hppfs_get_data : read failed, "
|
||||
"errno = %d\n", err);
|
||||
goto failed_free;
|
||||
} else if (n == 0)
|
||||
break;
|
||||
|
||||
*size_out += n;
|
||||
|
||||
if (n < sizeof(data->contents))
|
||||
break;
|
||||
|
||||
new = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (new == 0) {
|
||||
printk(KERN_ERR "hppfs_get_data : data allocation "
|
||||
"failed\n");
|
||||
err = -ENOMEM;
|
||||
goto failed_free;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
list_add(&new->list, &data->list);
|
||||
data = new;
|
||||
}
|
||||
return head;
|
||||
|
||||
failed_free:
|
||||
free_contents(head);
|
||||
failed:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct hppfs_private *hppfs_data(void)
|
||||
{
|
||||
struct hppfs_private *data;
|
||||
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return data;
|
||||
|
||||
*data = ((struct hppfs_private ) { .host_fd = -1,
|
||||
.len = -1,
|
||||
.contents = NULL } );
|
||||
return data;
|
||||
}
|
||||
|
||||
static int file_mode(int fmode)
|
||||
{
|
||||
if (fmode == (FMODE_READ | FMODE_WRITE))
|
||||
return O_RDWR;
|
||||
if (fmode == FMODE_READ)
|
||||
return O_RDONLY;
|
||||
if (fmode == FMODE_WRITE)
|
||||
return O_WRONLY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hppfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const struct cred *cred = file->f_cred;
|
||||
struct hppfs_private *data;
|
||||
struct path path;
|
||||
char *host_file;
|
||||
int err, fd, type, filter;
|
||||
|
||||
err = -ENOMEM;
|
||||
data = hppfs_data();
|
||||
if (data == NULL)
|
||||
goto out;
|
||||
|
||||
host_file = dentry_name(file->f_path.dentry, strlen("/rw"));
|
||||
if (host_file == NULL)
|
||||
goto out_free2;
|
||||
|
||||
path.mnt = inode->i_sb->s_fs_info;
|
||||
path.dentry = HPPFS_I(inode)->proc_dentry;
|
||||
|
||||
data->proc_file = dentry_open(&path, file_mode(file->f_mode), cred);
|
||||
err = PTR_ERR(data->proc_file);
|
||||
if (IS_ERR(data->proc_file))
|
||||
goto out_free1;
|
||||
|
||||
type = os_file_type(host_file);
|
||||
if (type == OS_TYPE_FILE) {
|
||||
fd = os_open_file(host_file, of_read(OPENFLAGS()), 0);
|
||||
if (fd >= 0)
|
||||
data->host_fd = fd;
|
||||
else
|
||||
printk(KERN_ERR "hppfs_open : failed to open '%s', "
|
||||
"errno = %d\n", host_file, -fd);
|
||||
|
||||
data->contents = NULL;
|
||||
} else if (type == OS_TYPE_DIR) {
|
||||
fd = open_host_sock(host_file, &filter);
|
||||
if (fd > 0) {
|
||||
data->contents = hppfs_get_data(fd, filter,
|
||||
data->proc_file,
|
||||
file, &data->len);
|
||||
if (!IS_ERR(data->contents))
|
||||
data->host_fd = fd;
|
||||
} else
|
||||
printk(KERN_ERR "hppfs_open : failed to open a socket "
|
||||
"in '%s', errno = %d\n", host_file, -fd);
|
||||
}
|
||||
kfree(host_file);
|
||||
|
||||
file->private_data = data;
|
||||
return 0;
|
||||
|
||||
out_free1:
|
||||
kfree(host_file);
|
||||
out_free2:
|
||||
free_contents(data->contents);
|
||||
kfree(data);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hppfs_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const struct cred *cred = file->f_cred;
|
||||
struct hppfs_private *data;
|
||||
struct path path;
|
||||
int err;
|
||||
|
||||
err = -ENOMEM;
|
||||
data = hppfs_data();
|
||||
if (data == NULL)
|
||||
goto out;
|
||||
|
||||
path.mnt = inode->i_sb->s_fs_info;
|
||||
path.dentry = HPPFS_I(inode)->proc_dentry;
|
||||
data->proc_file = dentry_open(&path, file_mode(file->f_mode), cred);
|
||||
err = PTR_ERR(data->proc_file);
|
||||
if (IS_ERR(data->proc_file))
|
||||
goto out_free;
|
||||
|
||||
file->private_data = data;
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
kfree(data);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static loff_t hppfs_llseek(struct file *file, loff_t off, int where)
|
||||
{
|
||||
struct hppfs_private *data = file->private_data;
|
||||
struct file *proc_file = data->proc_file;
|
||||
loff_t (*llseek)(struct file *, loff_t, int);
|
||||
loff_t ret;
|
||||
|
||||
llseek = file_inode(proc_file)->i_fop->llseek;
|
||||
if (llseek != NULL) {
|
||||
ret = (*llseek)(proc_file, off, where);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return default_llseek(file, off, where);
|
||||
}
|
||||
|
||||
static int hppfs_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hppfs_private *data = file->private_data;
|
||||
struct file *proc_file = data->proc_file;
|
||||
if (proc_file)
|
||||
fput(proc_file);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations hppfs_file_fops = {
|
||||
.owner = NULL,
|
||||
.llseek = hppfs_llseek,
|
||||
.read = hppfs_read,
|
||||
.write = hppfs_write,
|
||||
.open = hppfs_open,
|
||||
.release = hppfs_release,
|
||||
};
|
||||
|
||||
struct hppfs_dirent {
|
||||
struct dir_context ctx;
|
||||
struct dir_context *caller;
|
||||
struct dentry *dentry;
|
||||
};
|
||||
|
||||
static int hppfs_filldir(struct dir_context *ctx, const char *name, int size,
|
||||
loff_t offset, u64 inode, unsigned int type)
|
||||
{
|
||||
struct hppfs_dirent *dirent =
|
||||
container_of(ctx, struct hppfs_dirent, ctx);
|
||||
|
||||
if (file_removed(dirent->dentry, name))
|
||||
return 0;
|
||||
|
||||
dirent->caller->pos = dirent->ctx.pos;
|
||||
return !dir_emit(dirent->caller, name, size, inode, type);
|
||||
}
|
||||
|
||||
static int hppfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct hppfs_private *data = file->private_data;
|
||||
struct file *proc_file = data->proc_file;
|
||||
struct hppfs_dirent d = {
|
||||
.ctx.actor = hppfs_filldir,
|
||||
.caller = ctx,
|
||||
.dentry = file->f_path.dentry
|
||||
};
|
||||
int err;
|
||||
proc_file->f_pos = ctx->pos;
|
||||
err = iterate_dir(proc_file, &d.ctx);
|
||||
ctx->pos = d.ctx.pos;
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct file_operations hppfs_dir_fops = {
|
||||
.owner = NULL,
|
||||
.iterate = hppfs_readdir,
|
||||
.open = hppfs_dir_open,
|
||||
.llseek = default_llseek,
|
||||
.release = hppfs_release,
|
||||
};
|
||||
|
||||
static int hppfs_statfs(struct dentry *dentry, struct kstatfs *sf)
|
||||
{
|
||||
sf->f_blocks = 0;
|
||||
sf->f_bfree = 0;
|
||||
sf->f_bavail = 0;
|
||||
sf->f_files = 0;
|
||||
sf->f_ffree = 0;
|
||||
sf->f_type = HPPFS_SUPER_MAGIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct inode *hppfs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct hppfs_inode_info *hi;
|
||||
|
||||
hi = kmalloc(sizeof(*hi), GFP_KERNEL);
|
||||
if (!hi)
|
||||
return NULL;
|
||||
|
||||
hi->proc_dentry = NULL;
|
||||
inode_init_once(&hi->vfs_inode);
|
||||
return &hi->vfs_inode;
|
||||
}
|
||||
|
||||
void hppfs_evict_inode(struct inode *ino)
|
||||
{
|
||||
clear_inode(ino);
|
||||
dput(HPPFS_I(ino)->proc_dentry);
|
||||
mntput(ino->i_sb->s_fs_info);
|
||||
}
|
||||
|
||||
static void hppfs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
kfree(HPPFS_I(inode));
|
||||
}
|
||||
|
||||
static void hppfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
call_rcu(&inode->i_rcu, hppfs_i_callback);
|
||||
}
|
||||
|
||||
static const struct super_operations hppfs_sbops = {
|
||||
.alloc_inode = hppfs_alloc_inode,
|
||||
.destroy_inode = hppfs_destroy_inode,
|
||||
.evict_inode = hppfs_evict_inode,
|
||||
.statfs = hppfs_statfs,
|
||||
};
|
||||
|
||||
static int hppfs_readlink(struct dentry *dentry, char __user *buffer,
|
||||
int buflen)
|
||||
{
|
||||
struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry;
|
||||
return d_inode(proc_dentry)->i_op->readlink(proc_dentry, buffer,
|
||||
buflen);
|
||||
}
|
||||
|
||||
static const char *hppfs_follow_link(struct dentry *dentry, void **cookie)
|
||||
{
|
||||
struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry;
|
||||
|
||||
return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, cookie);
|
||||
}
|
||||
|
||||
static void hppfs_put_link(struct inode *inode, void *cookie)
|
||||
{
|
||||
struct inode *proc_inode = d_inode(HPPFS_I(inode)->proc_dentry);
|
||||
|
||||
if (proc_inode->i_op->put_link)
|
||||
proc_inode->i_op->put_link(proc_inode, cookie);
|
||||
}
|
||||
|
||||
static const struct inode_operations hppfs_dir_iops = {
|
||||
.lookup = hppfs_lookup,
|
||||
};
|
||||
|
||||
static const struct inode_operations hppfs_link_iops = {
|
||||
.readlink = hppfs_readlink,
|
||||
.follow_link = hppfs_follow_link,
|
||||
.put_link = hppfs_put_link,
|
||||
};
|
||||
|
||||
static struct inode *get_inode(struct super_block *sb, struct dentry *dentry)
|
||||
{
|
||||
struct inode *proc_ino = d_inode(dentry);
|
||||
struct inode *inode = new_inode(sb);
|
||||
|
||||
if (!inode) {
|
||||
dput(dentry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (d_is_dir(dentry)) {
|
||||
inode->i_op = &hppfs_dir_iops;
|
||||
inode->i_fop = &hppfs_dir_fops;
|
||||
} else if (d_is_symlink(dentry)) {
|
||||
inode->i_op = &hppfs_link_iops;
|
||||
inode->i_fop = &hppfs_file_fops;
|
||||
} else {
|
||||
inode->i_op = &hppfs_file_iops;
|
||||
inode->i_fop = &hppfs_file_fops;
|
||||
}
|
||||
|
||||
HPPFS_I(inode)->proc_dentry = dentry;
|
||||
|
||||
inode->i_uid = proc_ino->i_uid;
|
||||
inode->i_gid = proc_ino->i_gid;
|
||||
inode->i_atime = proc_ino->i_atime;
|
||||
inode->i_mtime = proc_ino->i_mtime;
|
||||
inode->i_ctime = proc_ino->i_ctime;
|
||||
inode->i_ino = proc_ino->i_ino;
|
||||
inode->i_mode = proc_ino->i_mode;
|
||||
set_nlink(inode, proc_ino->i_nlink);
|
||||
inode->i_size = proc_ino->i_size;
|
||||
inode->i_blocks = proc_ino->i_blocks;
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
static int hppfs_fill_super(struct super_block *sb, void *d, int silent)
|
||||
{
|
||||
struct inode *root_inode;
|
||||
struct vfsmount *proc_mnt;
|
||||
int err = -ENOENT;
|
||||
|
||||
proc_mnt = mntget(task_active_pid_ns(current)->proc_mnt);
|
||||
if (IS_ERR(proc_mnt))
|
||||
goto out;
|
||||
|
||||
sb->s_blocksize = 1024;
|
||||
sb->s_blocksize_bits = 10;
|
||||
sb->s_magic = HPPFS_SUPER_MAGIC;
|
||||
sb->s_op = &hppfs_sbops;
|
||||
sb->s_fs_info = proc_mnt;
|
||||
|
||||
err = -ENOMEM;
|
||||
root_inode = get_inode(sb, dget(proc_mnt->mnt_root));
|
||||
sb->s_root = d_make_root(root_inode);
|
||||
if (!sb->s_root)
|
||||
goto out_mntput;
|
||||
|
||||
return 0;
|
||||
|
||||
out_mntput:
|
||||
mntput(proc_mnt);
|
||||
out:
|
||||
return(err);
|
||||
}
|
||||
|
||||
static struct dentry *hppfs_read_super(struct file_system_type *type,
|
||||
int flags, const char *dev_name,
|
||||
void *data)
|
||||
{
|
||||
return mount_nodev(type, flags, data, hppfs_fill_super);
|
||||
}
|
||||
|
||||
static struct file_system_type hppfs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "hppfs",
|
||||
.mount = hppfs_read_super,
|
||||
.kill_sb = kill_anon_super,
|
||||
.fs_flags = 0,
|
||||
};
|
||||
MODULE_ALIAS_FS("hppfs");
|
||||
|
||||
static int __init init_hppfs(void)
|
||||
{
|
||||
return register_filesystem(&hppfs_type);
|
||||
}
|
||||
|
||||
static void __exit exit_hppfs(void)
|
||||
{
|
||||
unregister_filesystem(&hppfs_type);
|
||||
}
|
||||
|
||||
module_init(init_hppfs)
|
||||
module_exit(exit_hppfs)
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue