View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix;
19  
20  import java.io.File;
21  import java.io.FileDescriptor;
22  import java.io.IOException;
23  import java.io.RandomAccessFile;
24  import java.nio.channels.FileChannel;
25  import java.util.UUID;
26  import java.util.concurrent.atomic.AtomicBoolean;
27  
28  /**
29   * Hack to get a readable AND writable {@link FileChannel} for a {@link FileDescriptor}.
30   *
31   * @author Christian Kohlschütter
32   */
33  final class RAFChannelProvider extends RandomAccessFile implements FileDescriptorAccess {
34    private final File tempPath;
35    private final FileDescriptor fdObj;
36    private final FileDescriptor rafFdOrig = new FileDescriptor();
37    private final AtomicBoolean closed = new AtomicBoolean(false);
38  
39    private RAFChannelProvider(FileDescriptor fdObj) throws IOException {
40      this(fdObj, File.createTempFile("jux", ".sock"));
41    }
42  
43    private RAFChannelProvider(FileDescriptor fdObj, File tempPath) throws IOException {
44      super(tempPath, "rw");
45      this.tempPath = tempPath;
46      if (!tempPath.delete() && tempPath.exists()) {
47        if (!tempPath.delete()) {
48          // we tried our best (looking at you, Windows)
49        }
50        tempPath = new File(tempPath.getParentFile(), "jux-" + UUID.randomUUID().toString()
51            + ".sock");
52        if (tempPath.exists()) {
53          throw new IOException("Could not create a temporary path: " + tempPath);
54        }
55      }
56      tempPath.deleteOnExit();
57  
58      NativeUnixSocket.ensureSupported();
59  
60      this.fdObj = fdObj;
61  
62      FileDescriptor rafFdObj = getFD();
63      NativeUnixSocket.copyFileDescriptor(rafFdObj, rafFdOrig);
64      NativeUnixSocket.copyFileDescriptor(fdObj, rafFdObj);
65    }
66  
67    @Override
68    public FileDescriptor getFileDescriptor() {
69      return fdObj;
70    }
71  
72    @Override
73    public synchronized void close() throws IOException {
74      if (!closed.getAndSet(true)) {
75        NativeUnixSocket.copyFileDescriptor(rafFdOrig, getFD());
76        if (!tempPath.delete()) {
77          // we tried our best
78        }
79      }
80    }
81  
82    public static FileChannel getFileChannel(FileDescriptor fd) throws IOException {
83      return getFileChannel0(fd);
84    }
85  
86    @SuppressWarnings("resource")
87    private static FileChannel getFileChannel0(FileDescriptor fd) throws IOException {
88      return new RAFChannelProvider(fd).getChannel();
89    }
90  }