107 result.complete(null); |
108 result.complete(null); |
108 } |
109 } |
109 |
110 |
110 } |
111 } |
111 |
112 |
|
113 /** |
|
114 * A Subscriber that writes the flow of data to a given file. |
|
115 * |
|
116 * Privileged actions are performed within a limited doPrivileged that only |
|
117 * asserts the specific, write, file permissions that were checked during |
|
118 * the construction of this PathSubscriber. |
|
119 */ |
112 public static class PathSubscriber implements BodySubscriber<Path> { |
120 public static class PathSubscriber implements BodySubscriber<Path> { |
113 |
121 |
|
122 private static final FilePermission[] EMPTY_FILE_PERMISSIONS = new FilePermission[0]; |
|
123 |
114 private final Path file; |
124 private final Path file; |
|
125 private final OpenOption[] options; |
|
126 private final FilePermission[] filePermissions; |
115 private final CompletableFuture<Path> result = new MinimalFuture<>(); |
127 private final CompletableFuture<Path> result = new MinimalFuture<>(); |
116 private final OpenOption[] options; |
|
117 |
128 |
118 private volatile Flow.Subscription subscription; |
129 private volatile Flow.Subscription subscription; |
119 private volatile FileChannel out; |
130 private volatile FileChannel out; |
120 |
131 |
121 public PathSubscriber(Path file, List<OpenOption> options) { |
132 private static final String pathForSecurityCheck(Path path) { |
|
133 return path.toFile().getPath(); |
|
134 } |
|
135 |
|
136 /** |
|
137 * Factory for creating PathSubscriber. |
|
138 * |
|
139 * Permission checks are performed here before construction of the |
|
140 * PathSubscriber. Permission checking and construction are deliberately |
|
141 * and tightly co-located. |
|
142 */ |
|
143 public static PathSubscriber create(Path file, |
|
144 List<OpenOption> options) { |
|
145 FilePermission filePermission = null; |
|
146 SecurityManager sm = System.getSecurityManager(); |
|
147 if (sm != null) { |
|
148 String fn = pathForSecurityCheck(file); |
|
149 FilePermission writePermission = new FilePermission(fn, "write"); |
|
150 sm.checkPermission(writePermission); |
|
151 filePermission = writePermission; |
|
152 } |
|
153 return new PathSubscriber(file, options, filePermission); |
|
154 } |
|
155 |
|
156 // pp so handler implementations in the same package can construct |
|
157 /*package-private*/ PathSubscriber(Path file, |
|
158 List<OpenOption> options, |
|
159 FilePermission... filePermissions) { |
122 this.file = file; |
160 this.file = file; |
123 this.options = options.stream().toArray(OpenOption[]::new); |
161 this.options = options.stream().toArray(OpenOption[]::new); |
|
162 this.filePermissions = |
|
163 filePermissions == null ? EMPTY_FILE_PERMISSIONS : filePermissions; |
124 } |
164 } |
125 |
165 |
126 @Override |
166 @Override |
127 public void onSubscribe(Flow.Subscription subscription) { |
167 public void onSubscribe(Flow.Subscription subscription) { |
128 this.subscription = subscription; |
168 this.subscription = subscription; |
129 try { |
169 if (System.getSecurityManager() == null) { |
130 PrivilegedExceptionAction<FileChannel> pa = |
170 try { |
131 () -> FileChannel.open(file, options); |
171 out = FileChannel.open(file, options); |
132 out = AccessController.doPrivileged(pa); |
172 } catch (IOException ioe) { |
133 } catch (PrivilegedActionException pae) { |
173 result.completeExceptionally(ioe); |
134 Throwable t = pae.getCause() != null ? pae.getCause() : pae; |
174 return; |
135 result.completeExceptionally(t); |
175 } |
136 subscription.cancel(); |
176 } else { |
137 return; |
177 try { |
|
178 PrivilegedExceptionAction<FileChannel> pa = |
|
179 () -> FileChannel.open(file, options); |
|
180 out = AccessController.doPrivileged(pa, null, filePermissions); |
|
181 } catch (PrivilegedActionException pae) { |
|
182 Throwable t = pae.getCause() != null ? pae.getCause() : pae; |
|
183 result.completeExceptionally(t); |
|
184 subscription.cancel(); |
|
185 return; |
|
186 } |
138 } |
187 } |
139 subscription.request(1); |
188 subscription.request(1); |
140 } |
189 } |
141 |
190 |
142 @Override |
191 @Override |