Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
raspberrypi-kernel
提交
e99ca56c
R
raspberrypi-kernel
项目概览
openeuler
/
raspberrypi-kernel
通知
14
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
raspberrypi-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
e99ca56c
编写于
4月 08, 2017
作者:
A
Al Viro
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
move compat select-related syscalls to fs/select.c
Signed-off-by:
N
Al Viro
<
viro@zeniv.linux.org.uk
>
上级
2611dc19
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
419 addition
and
426 deletion
+419
-426
fs/compat.c
fs/compat.c
+0
-368
fs/select.c
fs/select.c
+419
-2
include/linux/poll.h
include/linux/poll.h
+0
-56
未找到文件。
fs/compat.c
浏览文件 @
e99ca56c
...
...
@@ -43,7 +43,6 @@
#include <linux/security.h>
#include <linux/highmem.h>
#include <linux/signal.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/fs_struct.h>
#include <linux/slab.h>
...
...
@@ -925,373 +924,6 @@ COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, fla
return
do_sys_open
(
dfd
,
filename
,
flags
,
mode
);
}
#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
static
int
poll_select_copy_remaining
(
struct
timespec
*
end_time
,
void
__user
*
p
,
int
timeval
,
int
ret
)
{
struct
timespec
ts
;
if
(
!
p
)
return
ret
;
if
(
current
->
personality
&
STICKY_TIMEOUTS
)
goto
sticky
;
/* No update for zero timeout */
if
(
!
end_time
->
tv_sec
&&
!
end_time
->
tv_nsec
)
return
ret
;
ktime_get_ts
(
&
ts
);
ts
=
timespec_sub
(
*
end_time
,
ts
);
if
(
ts
.
tv_sec
<
0
)
ts
.
tv_sec
=
ts
.
tv_nsec
=
0
;
if
(
timeval
)
{
struct
compat_timeval
rtv
;
rtv
.
tv_sec
=
ts
.
tv_sec
;
rtv
.
tv_usec
=
ts
.
tv_nsec
/
NSEC_PER_USEC
;
if
(
!
copy_to_user
(
p
,
&
rtv
,
sizeof
(
rtv
)))
return
ret
;
}
else
{
struct
compat_timespec
rts
;
rts
.
tv_sec
=
ts
.
tv_sec
;
rts
.
tv_nsec
=
ts
.
tv_nsec
;
if
(
!
copy_to_user
(
p
,
&
rts
,
sizeof
(
rts
)))
return
ret
;
}
/*
* If an application puts its timeval in read-only memory, we
* don't want the Linux-specific update to the timeval to
* cause a fault after the select has completed
* successfully. However, because we're not updating the
* timeval, we can't restart the system call.
*/
sticky:
if
(
ret
==
-
ERESTARTNOHAND
)
ret
=
-
EINTR
;
return
ret
;
}
/*
* Ooo, nasty. We need here to frob 32-bit unsigned longs to
* 64-bit unsigned longs.
*/
static
int
compat_get_fd_set
(
unsigned
long
nr
,
compat_ulong_t
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
nr
=
DIV_ROUND_UP
(
nr
,
__COMPAT_NFDBITS
);
if
(
ufdset
)
{
unsigned
long
odd
;
if
(
!
access_ok
(
VERIFY_WRITE
,
ufdset
,
nr
*
sizeof
(
compat_ulong_t
)))
return
-
EFAULT
;
odd
=
nr
&
1UL
;
nr
&=
~
1UL
;
while
(
nr
)
{
unsigned
long
h
,
l
;
if
(
__get_user
(
l
,
ufdset
)
||
__get_user
(
h
,
ufdset
+
1
))
return
-
EFAULT
;
ufdset
+=
2
;
*
fdset
++
=
h
<<
32
|
l
;
nr
-=
2
;
}
if
(
odd
&&
__get_user
(
*
fdset
,
ufdset
))
return
-
EFAULT
;
}
else
{
/* Tricky, must clear full unsigned long in the
* kernel fdset at the end, this makes sure that
* actually happens.
*/
memset
(
fdset
,
0
,
((
nr
+
1
)
&
~
1
)
*
sizeof
(
compat_ulong_t
));
}
return
0
;
}
static
int
compat_set_fd_set
(
unsigned
long
nr
,
compat_ulong_t
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
unsigned
long
odd
;
nr
=
DIV_ROUND_UP
(
nr
,
__COMPAT_NFDBITS
);
if
(
!
ufdset
)
return
0
;
odd
=
nr
&
1UL
;
nr
&=
~
1UL
;
while
(
nr
)
{
unsigned
long
h
,
l
;
l
=
*
fdset
++
;
h
=
l
>>
32
;
if
(
__put_user
(
l
,
ufdset
)
||
__put_user
(
h
,
ufdset
+
1
))
return
-
EFAULT
;
ufdset
+=
2
;
nr
-=
2
;
}
if
(
odd
&&
__put_user
(
*
fdset
,
ufdset
))
return
-
EFAULT
;
return
0
;
}
/*
* This is a virtual copy of sys_select from fs/select.c and probably
* should be compared to it from time to time
*/
/*
* We can actually return ERESTARTSYS instead of EINTR, but I'd
* like to be certain this leads to no problems. So I return
* EINTR just for safety.
*
* Update: ERESTARTSYS breaks at least the xview clock binary, so
* I'm trying ERESTARTNOHAND which restart only when you want to.
*/
int
compat_core_sys_select
(
int
n
,
compat_ulong_t
__user
*
inp
,
compat_ulong_t
__user
*
outp
,
compat_ulong_t
__user
*
exp
,
struct
timespec
*
end_time
)
{
fd_set_bits
fds
;
void
*
bits
;
int
size
,
max_fds
,
ret
=
-
EINVAL
;
struct
fdtable
*
fdt
;
long
stack_fds
[
SELECT_STACK_ALLOC
/
sizeof
(
long
)];
if
(
n
<
0
)
goto
out_nofds
;
/* max_fds can increase, so grab it once to avoid race */
rcu_read_lock
();
fdt
=
files_fdtable
(
current
->
files
);
max_fds
=
fdt
->
max_fds
;
rcu_read_unlock
();
if
(
n
>
max_fds
)
n
=
max_fds
;
/*
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
* since we used fdset we need to allocate memory in units of
* long-words.
*/
size
=
FDS_BYTES
(
n
);
bits
=
stack_fds
;
if
(
size
>
sizeof
(
stack_fds
)
/
6
)
{
bits
=
kmalloc
(
6
*
size
,
GFP_KERNEL
);
ret
=
-
ENOMEM
;
if
(
!
bits
)
goto
out_nofds
;
}
fds
.
in
=
(
unsigned
long
*
)
bits
;
fds
.
out
=
(
unsigned
long
*
)
(
bits
+
size
);
fds
.
ex
=
(
unsigned
long
*
)
(
bits
+
2
*
size
);
fds
.
res_in
=
(
unsigned
long
*
)
(
bits
+
3
*
size
);
fds
.
res_out
=
(
unsigned
long
*
)
(
bits
+
4
*
size
);
fds
.
res_ex
=
(
unsigned
long
*
)
(
bits
+
5
*
size
);
if
((
ret
=
compat_get_fd_set
(
n
,
inp
,
fds
.
in
))
||
(
ret
=
compat_get_fd_set
(
n
,
outp
,
fds
.
out
))
||
(
ret
=
compat_get_fd_set
(
n
,
exp
,
fds
.
ex
)))
goto
out
;
zero_fd_set
(
n
,
fds
.
res_in
);
zero_fd_set
(
n
,
fds
.
res_out
);
zero_fd_set
(
n
,
fds
.
res_ex
);
ret
=
do_select
(
n
,
&
fds
,
end_time
);
if
(
ret
<
0
)
goto
out
;
if
(
!
ret
)
{
ret
=
-
ERESTARTNOHAND
;
if
(
signal_pending
(
current
))
goto
out
;
ret
=
0
;
}
if
(
compat_set_fd_set
(
n
,
inp
,
fds
.
res_in
)
||
compat_set_fd_set
(
n
,
outp
,
fds
.
res_out
)
||
compat_set_fd_set
(
n
,
exp
,
fds
.
res_ex
))
ret
=
-
EFAULT
;
out:
if
(
bits
!=
stack_fds
)
kfree
(
bits
);
out_nofds:
return
ret
;
}
COMPAT_SYSCALL_DEFINE5
(
select
,
int
,
n
,
compat_ulong_t
__user
*
,
inp
,
compat_ulong_t
__user
*
,
outp
,
compat_ulong_t
__user
*
,
exp
,
struct
compat_timeval
__user
*
,
tvp
)
{
struct
timespec
end_time
,
*
to
=
NULL
;
struct
compat_timeval
tv
;
int
ret
;
if
(
tvp
)
{
if
(
copy_from_user
(
&
tv
,
tvp
,
sizeof
(
tv
)))
return
-
EFAULT
;
to
=
&
end_time
;
if
(
poll_select_set_timeout
(
to
,
tv
.
tv_sec
+
(
tv
.
tv_usec
/
USEC_PER_SEC
),
(
tv
.
tv_usec
%
USEC_PER_SEC
)
*
NSEC_PER_USEC
))
return
-
EINVAL
;
}
ret
=
compat_core_sys_select
(
n
,
inp
,
outp
,
exp
,
to
);
ret
=
poll_select_copy_remaining
(
&
end_time
,
tvp
,
1
,
ret
);
return
ret
;
}
struct
compat_sel_arg_struct
{
compat_ulong_t
n
;
compat_uptr_t
inp
;
compat_uptr_t
outp
;
compat_uptr_t
exp
;
compat_uptr_t
tvp
;
};
COMPAT_SYSCALL_DEFINE1
(
old_select
,
struct
compat_sel_arg_struct
__user
*
,
arg
)
{
struct
compat_sel_arg_struct
a
;
if
(
copy_from_user
(
&
a
,
arg
,
sizeof
(
a
)))
return
-
EFAULT
;
return
compat_sys_select
(
a
.
n
,
compat_ptr
(
a
.
inp
),
compat_ptr
(
a
.
outp
),
compat_ptr
(
a
.
exp
),
compat_ptr
(
a
.
tvp
));
}
static
long
do_compat_pselect
(
int
n
,
compat_ulong_t
__user
*
inp
,
compat_ulong_t
__user
*
outp
,
compat_ulong_t
__user
*
exp
,
struct
compat_timespec
__user
*
tsp
,
compat_sigset_t
__user
*
sigmask
,
compat_size_t
sigsetsize
)
{
compat_sigset_t
ss32
;
sigset_t
ksigmask
,
sigsaved
;
struct
compat_timespec
ts
;
struct
timespec
end_time
,
*
to
=
NULL
;
int
ret
;
if
(
tsp
)
{
if
(
copy_from_user
(
&
ts
,
tsp
,
sizeof
(
ts
)))
return
-
EFAULT
;
to
=
&
end_time
;
if
(
poll_select_set_timeout
(
to
,
ts
.
tv_sec
,
ts
.
tv_nsec
))
return
-
EINVAL
;
}
if
(
sigmask
)
{
if
(
sigsetsize
!=
sizeof
(
compat_sigset_t
))
return
-
EINVAL
;
if
(
copy_from_user
(
&
ss32
,
sigmask
,
sizeof
(
ss32
)))
return
-
EFAULT
;
sigset_from_compat
(
&
ksigmask
,
&
ss32
);
sigdelsetmask
(
&
ksigmask
,
sigmask
(
SIGKILL
)
|
sigmask
(
SIGSTOP
));
sigprocmask
(
SIG_SETMASK
,
&
ksigmask
,
&
sigsaved
);
}
ret
=
compat_core_sys_select
(
n
,
inp
,
outp
,
exp
,
to
);
ret
=
poll_select_copy_remaining
(
&
end_time
,
tsp
,
0
,
ret
);
if
(
ret
==
-
ERESTARTNOHAND
)
{
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if
(
sigmask
)
{
memcpy
(
&
current
->
saved_sigmask
,
&
sigsaved
,
sizeof
(
sigsaved
));
set_restore_sigmask
();
}
}
else
if
(
sigmask
)
sigprocmask
(
SIG_SETMASK
,
&
sigsaved
,
NULL
);
return
ret
;
}
COMPAT_SYSCALL_DEFINE6
(
pselect6
,
int
,
n
,
compat_ulong_t
__user
*
,
inp
,
compat_ulong_t
__user
*
,
outp
,
compat_ulong_t
__user
*
,
exp
,
struct
compat_timespec
__user
*
,
tsp
,
void
__user
*
,
sig
)
{
compat_size_t
sigsetsize
=
0
;
compat_uptr_t
up
=
0
;
if
(
sig
)
{
if
(
!
access_ok
(
VERIFY_READ
,
sig
,
sizeof
(
compat_uptr_t
)
+
sizeof
(
compat_size_t
))
||
__get_user
(
up
,
(
compat_uptr_t
__user
*
)
sig
)
||
__get_user
(
sigsetsize
,
(
compat_size_t
__user
*
)(
sig
+
sizeof
(
up
))))
return
-
EFAULT
;
}
return
do_compat_pselect
(
n
,
inp
,
outp
,
exp
,
tsp
,
compat_ptr
(
up
),
sigsetsize
);
}
COMPAT_SYSCALL_DEFINE5
(
ppoll
,
struct
pollfd
__user
*
,
ufds
,
unsigned
int
,
nfds
,
struct
compat_timespec
__user
*
,
tsp
,
const
compat_sigset_t
__user
*
,
sigmask
,
compat_size_t
,
sigsetsize
)
{
compat_sigset_t
ss32
;
sigset_t
ksigmask
,
sigsaved
;
struct
compat_timespec
ts
;
struct
timespec
end_time
,
*
to
=
NULL
;
int
ret
;
if
(
tsp
)
{
if
(
copy_from_user
(
&
ts
,
tsp
,
sizeof
(
ts
)))
return
-
EFAULT
;
to
=
&
end_time
;
if
(
poll_select_set_timeout
(
to
,
ts
.
tv_sec
,
ts
.
tv_nsec
))
return
-
EINVAL
;
}
if
(
sigmask
)
{
if
(
sigsetsize
!=
sizeof
(
compat_sigset_t
))
return
-
EINVAL
;
if
(
copy_from_user
(
&
ss32
,
sigmask
,
sizeof
(
ss32
)))
return
-
EFAULT
;
sigset_from_compat
(
&
ksigmask
,
&
ss32
);
sigdelsetmask
(
&
ksigmask
,
sigmask
(
SIGKILL
)
|
sigmask
(
SIGSTOP
));
sigprocmask
(
SIG_SETMASK
,
&
ksigmask
,
&
sigsaved
);
}
ret
=
do_sys_poll
(
ufds
,
nfds
,
to
);
/* We can restart this syscall, usually */
if
(
ret
==
-
EINTR
)
{
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if
(
sigmask
)
{
memcpy
(
&
current
->
saved_sigmask
,
&
sigsaved
,
sizeof
(
sigsaved
));
set_restore_sigmask
();
}
ret
=
-
ERESTARTNOHAND
;
}
else
if
(
sigmask
)
sigprocmask
(
SIG_SETMASK
,
&
sigsaved
,
NULL
);
ret
=
poll_select_copy_remaining
(
&
end_time
,
tsp
,
0
,
ret
);
return
ret
;
}
#ifdef CONFIG_FHANDLE
/*
* Exactly like fs/open.c:sys_open_by_handle_at(), except that it
...
...
fs/select.c
浏览文件 @
e99ca56c
...
...
@@ -338,6 +338,53 @@ static int poll_select_copy_remaining(struct timespec64 *end_time,
return
ret
;
}
/*
* Scalable version of the fd_set.
*/
typedef
struct
{
unsigned
long
*
in
,
*
out
,
*
ex
;
unsigned
long
*
res_in
,
*
res_out
,
*
res_ex
;
}
fd_set_bits
;
/*
* How many longwords for "nr" bits?
*/
#define FDS_BITPERLONG (8*sizeof(long))
#define FDS_LONGS(nr) (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
#define FDS_BYTES(nr) (FDS_LONGS(nr)*sizeof(long))
/*
* We do a VERIFY_WRITE here even though we are only reading this time:
* we'll write to it eventually..
*
* Use "unsigned long" accesses to let user-mode fd_set's be long-aligned.
*/
static
inline
int
get_fd_set
(
unsigned
long
nr
,
void
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
nr
=
FDS_BYTES
(
nr
);
if
(
ufdset
)
return
copy_from_user
(
fdset
,
ufdset
,
nr
)
?
-
EFAULT
:
0
;
memset
(
fdset
,
0
,
nr
);
return
0
;
}
static
inline
unsigned
long
__must_check
set_fd_set
(
unsigned
long
nr
,
void
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
if
(
ufdset
)
return
__copy_to_user
(
ufdset
,
fdset
,
FDS_BYTES
(
nr
));
return
0
;
}
static
inline
void
zero_fd_set
(
unsigned
long
nr
,
unsigned
long
*
fdset
)
{
memset
(
fdset
,
0
,
FDS_BYTES
(
nr
));
}
#define FDS_IN(fds, n) (fds->in + n)
#define FDS_OUT(fds, n) (fds->out + n)
#define FDS_EX(fds, n) (fds->ex + n)
...
...
@@ -401,7 +448,7 @@ static inline void wait_key_set(poll_table *wait, unsigned long in,
wait
->
_key
|=
POLLOUT_SET
;
}
int
do_select
(
int
n
,
fd_set_bits
*
fds
,
struct
timespec64
*
end_time
)
static
int
do_select
(
int
n
,
fd_set_bits
*
fds
,
struct
timespec64
*
end_time
)
{
ktime_t
expire
,
*
to
=
NULL
;
struct
poll_wqueues
table
;
...
...
@@ -881,7 +928,7 @@ static int do_poll(struct poll_list *list, struct poll_wqueues *wait,
#define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list)) / \
sizeof(struct pollfd))
int
do_sys_poll
(
struct
pollfd
__user
*
ufds
,
unsigned
int
nfds
,
static
int
do_sys_poll
(
struct
pollfd
__user
*
ufds
,
unsigned
int
nfds
,
struct
timespec64
*
end_time
)
{
struct
poll_wqueues
table
;
...
...
@@ -1053,3 +1100,373 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
return
ret
;
}
#ifdef CONFIG_COMPAT
#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
static
int
compat_poll_select_copy_remaining
(
struct
timespec
*
end_time
,
void
__user
*
p
,
int
timeval
,
int
ret
)
{
struct
timespec
ts
;
if
(
!
p
)
return
ret
;
if
(
current
->
personality
&
STICKY_TIMEOUTS
)
goto
sticky
;
/* No update for zero timeout */
if
(
!
end_time
->
tv_sec
&&
!
end_time
->
tv_nsec
)
return
ret
;
ktime_get_ts
(
&
ts
);
ts
=
timespec_sub
(
*
end_time
,
ts
);
if
(
ts
.
tv_sec
<
0
)
ts
.
tv_sec
=
ts
.
tv_nsec
=
0
;
if
(
timeval
)
{
struct
compat_timeval
rtv
;
rtv
.
tv_sec
=
ts
.
tv_sec
;
rtv
.
tv_usec
=
ts
.
tv_nsec
/
NSEC_PER_USEC
;
if
(
!
copy_to_user
(
p
,
&
rtv
,
sizeof
(
rtv
)))
return
ret
;
}
else
{
struct
compat_timespec
rts
;
rts
.
tv_sec
=
ts
.
tv_sec
;
rts
.
tv_nsec
=
ts
.
tv_nsec
;
if
(
!
copy_to_user
(
p
,
&
rts
,
sizeof
(
rts
)))
return
ret
;
}
/*
* If an application puts its timeval in read-only memory, we
* don't want the Linux-specific update to the timeval to
* cause a fault after the select has completed
* successfully. However, because we're not updating the
* timeval, we can't restart the system call.
*/
sticky:
if
(
ret
==
-
ERESTARTNOHAND
)
ret
=
-
EINTR
;
return
ret
;
}
/*
* Ooo, nasty. We need here to frob 32-bit unsigned longs to
* 64-bit unsigned longs.
*/
static
int
compat_get_fd_set
(
unsigned
long
nr
,
compat_ulong_t
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
nr
=
DIV_ROUND_UP
(
nr
,
__COMPAT_NFDBITS
);
if
(
ufdset
)
{
unsigned
long
odd
;
if
(
!
access_ok
(
VERIFY_WRITE
,
ufdset
,
nr
*
sizeof
(
compat_ulong_t
)))
return
-
EFAULT
;
odd
=
nr
&
1UL
;
nr
&=
~
1UL
;
while
(
nr
)
{
unsigned
long
h
,
l
;
if
(
__get_user
(
l
,
ufdset
)
||
__get_user
(
h
,
ufdset
+
1
))
return
-
EFAULT
;
ufdset
+=
2
;
*
fdset
++
=
h
<<
32
|
l
;
nr
-=
2
;
}
if
(
odd
&&
__get_user
(
*
fdset
,
ufdset
))
return
-
EFAULT
;
}
else
{
/* Tricky, must clear full unsigned long in the
* kernel fdset at the end, this makes sure that
* actually happens.
*/
memset
(
fdset
,
0
,
((
nr
+
1
)
&
~
1
)
*
sizeof
(
compat_ulong_t
));
}
return
0
;
}
static
int
compat_set_fd_set
(
unsigned
long
nr
,
compat_ulong_t
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
unsigned
long
odd
;
nr
=
DIV_ROUND_UP
(
nr
,
__COMPAT_NFDBITS
);
if
(
!
ufdset
)
return
0
;
odd
=
nr
&
1UL
;
nr
&=
~
1UL
;
while
(
nr
)
{
unsigned
long
h
,
l
;
l
=
*
fdset
++
;
h
=
l
>>
32
;
if
(
__put_user
(
l
,
ufdset
)
||
__put_user
(
h
,
ufdset
+
1
))
return
-
EFAULT
;
ufdset
+=
2
;
nr
-=
2
;
}
if
(
odd
&&
__put_user
(
*
fdset
,
ufdset
))
return
-
EFAULT
;
return
0
;
}
/*
* This is a virtual copy of sys_select from fs/select.c and probably
* should be compared to it from time to time
*/
/*
* We can actually return ERESTARTSYS instead of EINTR, but I'd
* like to be certain this leads to no problems. So I return
* EINTR just for safety.
*
* Update: ERESTARTSYS breaks at least the xview clock binary, so
* I'm trying ERESTARTNOHAND which restart only when you want to.
*/
static
int
compat_core_sys_select
(
int
n
,
compat_ulong_t
__user
*
inp
,
compat_ulong_t
__user
*
outp
,
compat_ulong_t
__user
*
exp
,
struct
timespec
*
end_time
)
{
fd_set_bits
fds
;
void
*
bits
;
int
size
,
max_fds
,
ret
=
-
EINVAL
;
struct
fdtable
*
fdt
;
long
stack_fds
[
SELECT_STACK_ALLOC
/
sizeof
(
long
)];
if
(
n
<
0
)
goto
out_nofds
;
/* max_fds can increase, so grab it once to avoid race */
rcu_read_lock
();
fdt
=
files_fdtable
(
current
->
files
);
max_fds
=
fdt
->
max_fds
;
rcu_read_unlock
();
if
(
n
>
max_fds
)
n
=
max_fds
;
/*
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
* since we used fdset we need to allocate memory in units of
* long-words.
*/
size
=
FDS_BYTES
(
n
);
bits
=
stack_fds
;
if
(
size
>
sizeof
(
stack_fds
)
/
6
)
{
bits
=
kmalloc
(
6
*
size
,
GFP_KERNEL
);
ret
=
-
ENOMEM
;
if
(
!
bits
)
goto
out_nofds
;
}
fds
.
in
=
(
unsigned
long
*
)
bits
;
fds
.
out
=
(
unsigned
long
*
)
(
bits
+
size
);
fds
.
ex
=
(
unsigned
long
*
)
(
bits
+
2
*
size
);
fds
.
res_in
=
(
unsigned
long
*
)
(
bits
+
3
*
size
);
fds
.
res_out
=
(
unsigned
long
*
)
(
bits
+
4
*
size
);
fds
.
res_ex
=
(
unsigned
long
*
)
(
bits
+
5
*
size
);
if
((
ret
=
compat_get_fd_set
(
n
,
inp
,
fds
.
in
))
||
(
ret
=
compat_get_fd_set
(
n
,
outp
,
fds
.
out
))
||
(
ret
=
compat_get_fd_set
(
n
,
exp
,
fds
.
ex
)))
goto
out
;
zero_fd_set
(
n
,
fds
.
res_in
);
zero_fd_set
(
n
,
fds
.
res_out
);
zero_fd_set
(
n
,
fds
.
res_ex
);
ret
=
do_select
(
n
,
&
fds
,
end_time
);
if
(
ret
<
0
)
goto
out
;
if
(
!
ret
)
{
ret
=
-
ERESTARTNOHAND
;
if
(
signal_pending
(
current
))
goto
out
;
ret
=
0
;
}
if
(
compat_set_fd_set
(
n
,
inp
,
fds
.
res_in
)
||
compat_set_fd_set
(
n
,
outp
,
fds
.
res_out
)
||
compat_set_fd_set
(
n
,
exp
,
fds
.
res_ex
))
ret
=
-
EFAULT
;
out:
if
(
bits
!=
stack_fds
)
kfree
(
bits
);
out_nofds:
return
ret
;
}
COMPAT_SYSCALL_DEFINE5
(
select
,
int
,
n
,
compat_ulong_t
__user
*
,
inp
,
compat_ulong_t
__user
*
,
outp
,
compat_ulong_t
__user
*
,
exp
,
struct
compat_timeval
__user
*
,
tvp
)
{
struct
timespec
end_time
,
*
to
=
NULL
;
struct
compat_timeval
tv
;
int
ret
;
if
(
tvp
)
{
if
(
copy_from_user
(
&
tv
,
tvp
,
sizeof
(
tv
)))
return
-
EFAULT
;
to
=
&
end_time
;
if
(
poll_select_set_timeout
(
to
,
tv
.
tv_sec
+
(
tv
.
tv_usec
/
USEC_PER_SEC
),
(
tv
.
tv_usec
%
USEC_PER_SEC
)
*
NSEC_PER_USEC
))
return
-
EINVAL
;
}
ret
=
compat_core_sys_select
(
n
,
inp
,
outp
,
exp
,
to
);
ret
=
compat_poll_select_copy_remaining
(
&
end_time
,
tvp
,
1
,
ret
);
return
ret
;
}
struct
compat_sel_arg_struct
{
compat_ulong_t
n
;
compat_uptr_t
inp
;
compat_uptr_t
outp
;
compat_uptr_t
exp
;
compat_uptr_t
tvp
;
};
COMPAT_SYSCALL_DEFINE1
(
old_select
,
struct
compat_sel_arg_struct
__user
*
,
arg
)
{
struct
compat_sel_arg_struct
a
;
if
(
copy_from_user
(
&
a
,
arg
,
sizeof
(
a
)))
return
-
EFAULT
;
return
compat_sys_select
(
a
.
n
,
compat_ptr
(
a
.
inp
),
compat_ptr
(
a
.
outp
),
compat_ptr
(
a
.
exp
),
compat_ptr
(
a
.
tvp
));
}
static
long
do_compat_pselect
(
int
n
,
compat_ulong_t
__user
*
inp
,
compat_ulong_t
__user
*
outp
,
compat_ulong_t
__user
*
exp
,
struct
compat_timespec
__user
*
tsp
,
compat_sigset_t
__user
*
sigmask
,
compat_size_t
sigsetsize
)
{
compat_sigset_t
ss32
;
sigset_t
ksigmask
,
sigsaved
;
struct
compat_timespec
ts
;
struct
timespec
end_time
,
*
to
=
NULL
;
int
ret
;
if
(
tsp
)
{
if
(
copy_from_user
(
&
ts
,
tsp
,
sizeof
(
ts
)))
return
-
EFAULT
;
to
=
&
end_time
;
if
(
poll_select_set_timeout
(
to
,
ts
.
tv_sec
,
ts
.
tv_nsec
))
return
-
EINVAL
;
}
if
(
sigmask
)
{
if
(
sigsetsize
!=
sizeof
(
compat_sigset_t
))
return
-
EINVAL
;
if
(
copy_from_user
(
&
ss32
,
sigmask
,
sizeof
(
ss32
)))
return
-
EFAULT
;
sigset_from_compat
(
&
ksigmask
,
&
ss32
);
sigdelsetmask
(
&
ksigmask
,
sigmask
(
SIGKILL
)
|
sigmask
(
SIGSTOP
));
sigprocmask
(
SIG_SETMASK
,
&
ksigmask
,
&
sigsaved
);
}
ret
=
compat_core_sys_select
(
n
,
inp
,
outp
,
exp
,
to
);
ret
=
compat_poll_select_copy_remaining
(
&
end_time
,
tsp
,
0
,
ret
);
if
(
ret
==
-
ERESTARTNOHAND
)
{
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if
(
sigmask
)
{
memcpy
(
&
current
->
saved_sigmask
,
&
sigsaved
,
sizeof
(
sigsaved
));
set_restore_sigmask
();
}
}
else
if
(
sigmask
)
sigprocmask
(
SIG_SETMASK
,
&
sigsaved
,
NULL
);
return
ret
;
}
COMPAT_SYSCALL_DEFINE6
(
pselect6
,
int
,
n
,
compat_ulong_t
__user
*
,
inp
,
compat_ulong_t
__user
*
,
outp
,
compat_ulong_t
__user
*
,
exp
,
struct
compat_timespec
__user
*
,
tsp
,
void
__user
*
,
sig
)
{
compat_size_t
sigsetsize
=
0
;
compat_uptr_t
up
=
0
;
if
(
sig
)
{
if
(
!
access_ok
(
VERIFY_READ
,
sig
,
sizeof
(
compat_uptr_t
)
+
sizeof
(
compat_size_t
))
||
__get_user
(
up
,
(
compat_uptr_t
__user
*
)
sig
)
||
__get_user
(
sigsetsize
,
(
compat_size_t
__user
*
)(
sig
+
sizeof
(
up
))))
return
-
EFAULT
;
}
return
do_compat_pselect
(
n
,
inp
,
outp
,
exp
,
tsp
,
compat_ptr
(
up
),
sigsetsize
);
}
COMPAT_SYSCALL_DEFINE5
(
ppoll
,
struct
pollfd
__user
*
,
ufds
,
unsigned
int
,
nfds
,
struct
compat_timespec
__user
*
,
tsp
,
const
compat_sigset_t
__user
*
,
sigmask
,
compat_size_t
,
sigsetsize
)
{
compat_sigset_t
ss32
;
sigset_t
ksigmask
,
sigsaved
;
struct
compat_timespec
ts
;
struct
timespec
end_time
,
*
to
=
NULL
;
int
ret
;
if
(
tsp
)
{
if
(
copy_from_user
(
&
ts
,
tsp
,
sizeof
(
ts
)))
return
-
EFAULT
;
to
=
&
end_time
;
if
(
poll_select_set_timeout
(
to
,
ts
.
tv_sec
,
ts
.
tv_nsec
))
return
-
EINVAL
;
}
if
(
sigmask
)
{
if
(
sigsetsize
!=
sizeof
(
compat_sigset_t
))
return
-
EINVAL
;
if
(
copy_from_user
(
&
ss32
,
sigmask
,
sizeof
(
ss32
)))
return
-
EFAULT
;
sigset_from_compat
(
&
ksigmask
,
&
ss32
);
sigdelsetmask
(
&
ksigmask
,
sigmask
(
SIGKILL
)
|
sigmask
(
SIGSTOP
));
sigprocmask
(
SIG_SETMASK
,
&
ksigmask
,
&
sigsaved
);
}
ret
=
do_sys_poll
(
ufds
,
nfds
,
to
);
/* We can restart this syscall, usually */
if
(
ret
==
-
EINTR
)
{
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if
(
sigmask
)
{
memcpy
(
&
current
->
saved_sigmask
,
&
sigsaved
,
sizeof
(
sigsaved
));
set_restore_sigmask
();
}
ret
=
-
ERESTARTNOHAND
;
}
else
if
(
sigmask
)
sigprocmask
(
SIG_SETMASK
,
&
sigsaved
,
NULL
);
ret
=
compat_poll_select_copy_remaining
(
&
end_time
,
tsp
,
0
,
ret
);
return
ret
;
}
#endif
include/linux/poll.h
浏览文件 @
e99ca56c
...
...
@@ -98,64 +98,8 @@ extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
ktime_t
*
expires
,
unsigned
long
slack
);
extern
u64
select_estimate_accuracy
(
struct
timespec64
*
tv
);
static
inline
int
poll_schedule
(
struct
poll_wqueues
*
pwq
,
int
state
)
{
return
poll_schedule_timeout
(
pwq
,
state
,
NULL
,
0
);
}
/*
* Scalable version of the fd_set.
*/
typedef
struct
{
unsigned
long
*
in
,
*
out
,
*
ex
;
unsigned
long
*
res_in
,
*
res_out
,
*
res_ex
;
}
fd_set_bits
;
/*
* How many longwords for "nr" bits?
*/
#define FDS_BITPERLONG (8*sizeof(long))
#define FDS_LONGS(nr) (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
#define FDS_BYTES(nr) (FDS_LONGS(nr)*sizeof(long))
/*
* We do a VERIFY_WRITE here even though we are only reading this time:
* we'll write to it eventually..
*
* Use "unsigned long" accesses to let user-mode fd_set's be long-aligned.
*/
static
inline
int
get_fd_set
(
unsigned
long
nr
,
void
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
nr
=
FDS_BYTES
(
nr
);
if
(
ufdset
)
return
copy_from_user
(
fdset
,
ufdset
,
nr
)
?
-
EFAULT
:
0
;
memset
(
fdset
,
0
,
nr
);
return
0
;
}
static
inline
unsigned
long
__must_check
set_fd_set
(
unsigned
long
nr
,
void
__user
*
ufdset
,
unsigned
long
*
fdset
)
{
if
(
ufdset
)
return
__copy_to_user
(
ufdset
,
fdset
,
FDS_BYTES
(
nr
));
return
0
;
}
static
inline
void
zero_fd_set
(
unsigned
long
nr
,
unsigned
long
*
fdset
)
{
memset
(
fdset
,
0
,
FDS_BYTES
(
nr
));
}
#define MAX_INT64_SECONDS (((s64)(~((u64)0)>>1)/HZ)-1)
extern
int
do_select
(
int
n
,
fd_set_bits
*
fds
,
struct
timespec64
*
end_time
);
extern
int
do_sys_poll
(
struct
pollfd
__user
*
ufds
,
unsigned
int
nfds
,
struct
timespec64
*
end_time
);
extern
int
core_sys_select
(
int
n
,
fd_set
__user
*
inp
,
fd_set
__user
*
outp
,
fd_set
__user
*
exp
,
struct
timespec64
*
end_time
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录