1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.io;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.system.ErrnoException;
21 import android.system.GaiException;
22 import android.system.Int64Ref;
23 import android.system.OsConstants;
24 import android.system.StructAddrinfo;
25 import android.system.StructLinger;
26 import android.system.StructPollfd;
27 import android.system.StructStat;
28 import android.system.StructStatVfs;
29 
30 import java.io.FileDescriptor;
31 import java.io.InterruptedIOException;
32 import java.net.InetAddress;
33 import java.net.InetSocketAddress;
34 import java.net.SocketAddress;
35 import java.net.SocketException;
36 import java.nio.ByteBuffer;
37 
38 import dalvik.system.BlockGuard;
39 import dalvik.system.SocketTagger;
40 
41 import static android.system.OsConstants.*;
42 
43 /**
44  * Informs BlockGuard of any activity it should be aware of.
45  */
46 public class BlockGuardOs extends ForwardingOs {
47     @UnsupportedAppUsage
BlockGuardOs(Os os)48     public BlockGuardOs(Os os) {
49         super(os);
50     }
51 
tagSocket(FileDescriptor fd)52     private FileDescriptor tagSocket(FileDescriptor fd) throws ErrnoException {
53         try {
54             SocketTagger.get().tag(fd);
55             return fd;
56         } catch (SocketException e) {
57             throw new ErrnoException("socket", EINVAL, e);
58         }
59     }
60 
accept(FileDescriptor fd, SocketAddress peerAddress)61     @Override public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException {
62         BlockGuard.getThreadPolicy().onNetwork();
63         final FileDescriptor acceptFd = super.accept(fd, peerAddress);
64         if (isInetSocket(acceptFd)) {
65             tagSocket(acceptFd);
66         }
67         return acceptFd;
68     }
69 
access(String path, int mode)70     @Override public boolean access(String path, int mode) throws ErrnoException {
71         BlockGuard.getThreadPolicy().onReadFromDisk();
72         BlockGuard.getVmPolicy().onPathAccess(path);
73         return super.access(path, mode);
74     }
75 
76     @UnsupportedAppUsage
chmod(String path, int mode)77     @Override public void chmod(String path, int mode) throws ErrnoException {
78         BlockGuard.getThreadPolicy().onWriteToDisk();
79         BlockGuard.getVmPolicy().onPathAccess(path);
80         super.chmod(path, mode);
81     }
82 
83     @UnsupportedAppUsage
chown(String path, int uid, int gid)84     @Override public void chown(String path, int uid, int gid) throws ErrnoException {
85         BlockGuard.getThreadPolicy().onWriteToDisk();
86         BlockGuard.getVmPolicy().onPathAccess(path);
87         super.chown(path, uid, gid);
88     }
89 
90     @UnsupportedAppUsage
close(FileDescriptor fd)91     @Override public void close(FileDescriptor fd) throws ErrnoException {
92         try {
93             // The usual case is that this _isn't_ a socket, so the getsockopt(2) call in
94             // isLingerSocket will throw, and that's really expensive. Try to avoid asking
95             // if we don't care.
96             if (fd.isSocket$()) {
97                 if (isLingerSocket(fd)) {
98                     // If the fd is a socket with SO_LINGER set, we might block indefinitely.
99                     // We allow non-linger sockets so that apps can close their network
100                     // connections in methods like onDestroy which will run on the UI thread.
101                     BlockGuard.getThreadPolicy().onNetwork();
102                 }
103             }
104         } catch (ErrnoException ignored) {
105             // We're called via Socket.close (which doesn't ask for us to be called), so we
106             // must not throw here, because Socket.close must not throw if asked to close an
107             // already-closed socket. Also, the passed-in FileDescriptor isn't necessarily
108             // a socket at all.
109         }
110         super.close(fd);
111     }
112 
isInetSocket(FileDescriptor fd)113     private static boolean isInetSocket(FileDescriptor fd) throws ErrnoException{
114         return isInetDomain(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_DOMAIN));
115     }
116 
isInetDomain(int domain)117     private static boolean isInetDomain(int domain) {
118         return (domain == AF_INET) || (domain == AF_INET6);
119     }
120 
isLingerSocket(FileDescriptor fd)121     private static boolean isLingerSocket(FileDescriptor fd) throws ErrnoException {
122         StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
123         return linger.isOn() && linger.l_linger > 0;
124     }
125 
isUdpSocket(FileDescriptor fd)126     private static boolean isUdpSocket(FileDescriptor fd) throws ErrnoException {
127         return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL) == IPPROTO_UDP;
128     }
129 
connect(FileDescriptor fd, InetAddress address, int port)130     @Override public void connect(FileDescriptor fd, InetAddress address, int port)
131             throws ErrnoException, SocketException {
132         boolean skipGuard = false;
133         try {
134             skipGuard = isUdpSocket(fd);
135         } catch (ErrnoException ignored) {
136         }
137         if (!skipGuard) BlockGuard.getThreadPolicy().onNetwork();
138         super.connect(fd, address, port);
139     }
140 
connect(FileDescriptor fd, SocketAddress address)141     @Override public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException,
142             SocketException {
143         boolean skipGuard = false;
144         try {
145             skipGuard = isUdpSocket(fd);
146         } catch (ErrnoException ignored) {
147         }
148         if (!skipGuard) BlockGuard.getThreadPolicy().onNetwork();
149         super.connect(fd, address);
150     }
151 
152     @UnsupportedAppUsage
fchmod(FileDescriptor fd, int mode)153     @Override public void fchmod(FileDescriptor fd, int mode) throws ErrnoException {
154         BlockGuard.getThreadPolicy().onWriteToDisk();
155         super.fchmod(fd, mode);
156     }
157 
158     @UnsupportedAppUsage
fchown(FileDescriptor fd, int uid, int gid)159     @Override public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException {
160         BlockGuard.getThreadPolicy().onWriteToDisk();
161         super.fchown(fd, uid, gid);
162     }
163 
164     // TODO: Untag newFd when needed for dup2(FileDescriptor oldFd, int newFd)
165 
166     @UnsupportedAppUsage
fdatasync(FileDescriptor fd)167     @Override public void fdatasync(FileDescriptor fd) throws ErrnoException {
168         BlockGuard.getThreadPolicy().onWriteToDisk();
169         super.fdatasync(fd);
170     }
171 
172     @UnsupportedAppUsage
fstat(FileDescriptor fd)173     @Override public StructStat fstat(FileDescriptor fd) throws ErrnoException {
174         BlockGuard.getThreadPolicy().onReadFromDisk();
175         return super.fstat(fd);
176     }
177 
178     @UnsupportedAppUsage
fstatvfs(FileDescriptor fd)179     @Override public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException {
180         BlockGuard.getThreadPolicy().onReadFromDisk();
181         return super.fstatvfs(fd);
182     }
183 
fsync(FileDescriptor fd)184     @Override public void fsync(FileDescriptor fd) throws ErrnoException {
185         BlockGuard.getThreadPolicy().onWriteToDisk();
186         super.fsync(fd);
187     }
188 
ftruncate(FileDescriptor fd, long length)189     @Override public void ftruncate(FileDescriptor fd, long length) throws ErrnoException {
190         BlockGuard.getThreadPolicy().onWriteToDisk();
191         super.ftruncate(fd, length);
192     }
193 
android_getaddrinfo(String node, StructAddrinfo hints, int netId)194     @Override public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException {
195         // With AI_NUMERICHOST flag set, the node must a numerical network address, therefore no
196         // host address lookups will be performed. In this case, it is fine to perform on main
197         // thread.
198         boolean isNumericHost = (hints.ai_flags & AI_NUMERICHOST) != 0;
199         if (!isNumericHost) {
200             BlockGuard.getThreadPolicy().onNetwork();
201         }
202         return super.android_getaddrinfo(node, hints, netId);
203     }
204 
205     @UnsupportedAppUsage
lchown(String path, int uid, int gid)206     @Override public void lchown(String path, int uid, int gid) throws ErrnoException {
207         BlockGuard.getThreadPolicy().onWriteToDisk();
208         BlockGuard.getVmPolicy().onPathAccess(path);
209         super.lchown(path, uid, gid);
210     }
211 
212     @UnsupportedAppUsage
link(String oldPath, String newPath)213     @Override public void link(String oldPath, String newPath) throws ErrnoException {
214         BlockGuard.getThreadPolicy().onWriteToDisk();
215         BlockGuard.getVmPolicy().onPathAccess(oldPath);
216         BlockGuard.getVmPolicy().onPathAccess(newPath);
217         super.link(oldPath, newPath);
218     }
219 
220     @UnsupportedAppUsage
lseek(FileDescriptor fd, long offset, int whence)221     @Override public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
222         BlockGuard.getThreadPolicy().onReadFromDisk();
223         return super.lseek(fd, offset, whence);
224     }
225 
226     @UnsupportedAppUsage
lstat(String path)227     @Override public StructStat lstat(String path) throws ErrnoException {
228         BlockGuard.getThreadPolicy().onReadFromDisk();
229         BlockGuard.getVmPolicy().onPathAccess(path);
230         return super.lstat(path);
231     }
232 
233     @UnsupportedAppUsage
mkdir(String path, int mode)234     @Override public void mkdir(String path, int mode) throws ErrnoException {
235         BlockGuard.getThreadPolicy().onWriteToDisk();
236         BlockGuard.getVmPolicy().onPathAccess(path);
237         super.mkdir(path, mode);
238     }
239 
240     @UnsupportedAppUsage
mkfifo(String path, int mode)241     @Override public void mkfifo(String path, int mode) throws ErrnoException {
242         BlockGuard.getThreadPolicy().onWriteToDisk();
243         BlockGuard.getVmPolicy().onPathAccess(path);
244         super.mkfifo(path, mode);
245     }
246 
247     @UnsupportedAppUsage
open(String path, int flags, int mode)248     @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
249         BlockGuard.getThreadPolicy().onReadFromDisk();
250         BlockGuard.getVmPolicy().onPathAccess(path);
251         if ((flags & O_ACCMODE) != O_RDONLY) {
252             BlockGuard.getThreadPolicy().onWriteToDisk();
253         }
254         return super.open(path, flags, mode);
255     }
256 
poll(StructPollfd[] fds, int timeoutMs)257     @Override public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException {
258         // Greater than 0 is a timeout in milliseconds and -1 means "block forever",
259         // but 0 means "poll and return immediately", which shouldn't be subject to BlockGuard.
260         if (timeoutMs != 0) {
261             BlockGuard.getThreadPolicy().onNetwork();
262         }
263         return super.poll(fds, timeoutMs);
264     }
265 
266     @UnsupportedAppUsage
posix_fallocate(FileDescriptor fd, long offset, long length)267     @Override public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException {
268         BlockGuard.getThreadPolicy().onWriteToDisk();
269         super.posix_fallocate(fd, offset, length);
270     }
271 
272     @UnsupportedAppUsage
pread(FileDescriptor fd, ByteBuffer buffer, long offset)273     @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
274         BlockGuard.getThreadPolicy().onReadFromDisk();
275         return super.pread(fd, buffer, offset);
276     }
277 
278     @UnsupportedAppUsage
pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset)279     @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
280         BlockGuard.getThreadPolicy().onReadFromDisk();
281         return super.pread(fd, bytes, byteOffset, byteCount, offset);
282     }
283 
284     @UnsupportedAppUsage
pwrite(FileDescriptor fd, ByteBuffer buffer, long offset)285     @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
286         BlockGuard.getThreadPolicy().onWriteToDisk();
287         return super.pwrite(fd, buffer, offset);
288     }
289 
290     @UnsupportedAppUsage
pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset)291     @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
292         BlockGuard.getThreadPolicy().onWriteToDisk();
293         return super.pwrite(fd, bytes, byteOffset, byteCount, offset);
294     }
295 
296     @UnsupportedAppUsage
read(FileDescriptor fd, ByteBuffer buffer)297     @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
298         BlockGuard.getThreadPolicy().onReadFromDisk();
299         return super.read(fd, buffer);
300     }
301 
302     @UnsupportedAppUsage
read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount)303     @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
304         BlockGuard.getThreadPolicy().onReadFromDisk();
305         return super.read(fd, bytes, byteOffset, byteCount);
306     }
307 
308     @UnsupportedAppUsage
readlink(String path)309     @Override public String readlink(String path) throws ErrnoException {
310       BlockGuard.getThreadPolicy().onReadFromDisk();
311       BlockGuard.getVmPolicy().onPathAccess(path);
312       return super.readlink(path);
313     }
314 
315     @UnsupportedAppUsage
realpath(String path)316     @Override public String realpath(String path) throws ErrnoException {
317       BlockGuard.getThreadPolicy().onReadFromDisk();
318       BlockGuard.getVmPolicy().onPathAccess(path);
319       return super.realpath(path);
320     }
321 
322     @UnsupportedAppUsage
readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts)323     @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
324         BlockGuard.getThreadPolicy().onReadFromDisk();
325         return super.readv(fd, buffers, offsets, byteCounts);
326     }
327 
recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress)328     @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
329         BlockGuard.getThreadPolicy().onNetwork();
330         return super.recvfrom(fd, buffer, flags, srcAddress);
331     }
332 
recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress)333     @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
334         BlockGuard.getThreadPolicy().onNetwork();
335         return super.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
336     }
337 
338     @UnsupportedAppUsage
remove(String path)339     @Override public void remove(String path) throws ErrnoException {
340         BlockGuard.getThreadPolicy().onWriteToDisk();
341         BlockGuard.getVmPolicy().onPathAccess(path);
342         super.remove(path);
343     }
344 
345     @UnsupportedAppUsage
rename(String oldPath, String newPath)346     @Override public void rename(String oldPath, String newPath) throws ErrnoException {
347         BlockGuard.getThreadPolicy().onWriteToDisk();
348         BlockGuard.getVmPolicy().onPathAccess(oldPath);
349         BlockGuard.getVmPolicy().onPathAccess(newPath);
350         super.rename(oldPath, newPath);
351     }
352 
sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount)353     @Override public long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException {
354         BlockGuard.getThreadPolicy().onWriteToDisk();
355         return super.sendfile(outFd, inFd, offset, byteCount);
356     }
357 
sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port)358     @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
359         BlockGuard.getThreadPolicy().onNetwork();
360         return super.sendto(fd, buffer, flags, inetAddress, port);
361     }
362 
sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port)363     @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
364         // We permit datagrams without hostname lookups.
365         if (inetAddress != null) {
366             BlockGuard.getThreadPolicy().onNetwork();
367         }
368         return super.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
369     }
370 
socket(int domain, int type, int protocol)371     @Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {
372         final FileDescriptor fd = super.socket(domain, type, protocol);
373         if (isInetDomain(domain)) {
374             tagSocket(fd);
375         }
376         return fd;
377     }
378 
socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2)379     @Override public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException {
380         super.socketpair(domain, type, protocol, fd1, fd2);
381         if (isInetDomain(domain)) {
382             tagSocket(fd1);
383             tagSocket(fd2);
384         }
385     }
386 
387     @UnsupportedAppUsage
stat(String path)388     @Override public StructStat stat(String path) throws ErrnoException {
389         BlockGuard.getThreadPolicy().onReadFromDisk();
390         BlockGuard.getVmPolicy().onPathAccess(path);
391         return super.stat(path);
392     }
393 
394     @UnsupportedAppUsage
statvfs(String path)395     @Override public StructStatVfs statvfs(String path) throws ErrnoException {
396         BlockGuard.getThreadPolicy().onReadFromDisk();
397         BlockGuard.getVmPolicy().onPathAccess(path);
398         return super.statvfs(path);
399     }
400 
401     @UnsupportedAppUsage
symlink(String oldPath, String newPath)402     @Override public void symlink(String oldPath, String newPath) throws ErrnoException {
403         BlockGuard.getThreadPolicy().onWriteToDisk();
404         BlockGuard.getVmPolicy().onPathAccess(oldPath);
405         BlockGuard.getVmPolicy().onPathAccess(newPath);
406         super.symlink(oldPath, newPath);
407     }
408 
409     @UnsupportedAppUsage
write(FileDescriptor fd, ByteBuffer buffer)410     @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
411         BlockGuard.getThreadPolicy().onWriteToDisk();
412         return super.write(fd, buffer);
413     }
414 
415     @UnsupportedAppUsage
write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount)416     @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
417         BlockGuard.getThreadPolicy().onWriteToDisk();
418         return super.write(fd, bytes, byteOffset, byteCount);
419     }
420 
421     @UnsupportedAppUsage
writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts)422     @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
423         BlockGuard.getThreadPolicy().onWriteToDisk();
424         return super.writev(fd, buffers, offsets, byteCounts);
425     }
426 
execv(String filename, String[] argv)427     @Override public void execv(String filename, String[] argv) throws ErrnoException {
428         BlockGuard.getThreadPolicy().onReadFromDisk();
429         BlockGuard.getVmPolicy().onPathAccess(filename);
430         super.execv(filename, argv);
431     }
432 
execve(String filename, String[] argv, String[] envp)433     @Override public void execve(String filename, String[] argv, String[] envp)
434             throws ErrnoException {
435         BlockGuard.getThreadPolicy().onReadFromDisk();
436         BlockGuard.getVmPolicy().onPathAccess(filename);
437         super.execve(filename, argv, envp);
438     }
439 
getxattr(String path, String name)440     @Override public byte[] getxattr(String path, String name) throws ErrnoException {
441         BlockGuard.getThreadPolicy().onReadFromDisk();
442         BlockGuard.getVmPolicy().onPathAccess(path);
443         return super.getxattr(path, name);
444     }
445 
msync(long address, long byteCount, int flags)446     @Override public void msync(long address, long byteCount, int flags) throws ErrnoException {
447         if ((flags & OsConstants.MS_SYNC) != 0) {
448             BlockGuard.getThreadPolicy().onWriteToDisk();
449         }
450         super.msync(address, byteCount, flags);
451     }
452 
removexattr(String path, String name)453     @Override public void removexattr(String path, String name) throws ErrnoException {
454         BlockGuard.getThreadPolicy().onWriteToDisk();
455         BlockGuard.getVmPolicy().onPathAccess(path);
456         super.removexattr(path, name);
457     }
458 
setxattr(String path, String name, byte[] value, int flags)459     @Override public void setxattr(String path, String name, byte[] value, int flags)
460             throws ErrnoException {
461         BlockGuard.getThreadPolicy().onWriteToDisk();
462         BlockGuard.getVmPolicy().onPathAccess(path);
463         super.setxattr(path, name, value, flags);
464     }
465 
sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address)466     @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount,
467             int flags, SocketAddress address) throws ErrnoException, SocketException {
468         BlockGuard.getThreadPolicy().onNetwork();
469         return super.sendto(fd, bytes, byteOffset, byteCount, flags, address);
470     }
471 
unlink(String pathname)472     @Override public void unlink(String pathname) throws ErrnoException {
473         BlockGuard.getThreadPolicy().onWriteToDisk();
474         BlockGuard.getVmPolicy().onPathAccess(pathname);
475         super.unlink(pathname);
476     }
477 
splice(FileDescriptor fdIn, Int64Ref offIn, FileDescriptor fdOut, Int64Ref offOut, long len, int flags)478     @Override public long splice(FileDescriptor fdIn, Int64Ref offIn, FileDescriptor fdOut, Int64Ref offOut, long len, int flags) throws ErrnoException {
479         // It's infeasible to figure out if splice will result in read or write (would require fstat to figure out which fd is pipe).
480         // So, signal both read and write.
481         BlockGuard.getThreadPolicy().onWriteToDisk();
482         BlockGuard.getThreadPolicy().onReadFromDisk();
483         return super.splice(fdIn, offIn, fdOut, offOut, len, flags);
484     }
485 }
486