Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
e99ca56c
Commit
e99ca56c
authored
Apr 08, 2017
by
Al Viro
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
move compat select-related syscalls to fs/select.c
Signed-off-by:
Al Viro
<
viro@zeniv.linux.org.uk
>
parent
2611dc19
Changes
3
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
419 additions
and
426 deletions
+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
No files found.
fs/compat.c
View file @
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
View file @
e99ca56c
This diff is collapsed.
Click to expand it.
include/linux/poll.h
View file @
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
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment