This project has retired. For details please refer to its
Attic page.
FilesystemAsset xref
1 package org.apache.archiva.repository.storage;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.lang3.StringUtils;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.nio.channels.FileChannel;
30 import java.nio.channels.ReadableByteChannel;
31 import java.nio.channels.WritableByteChannel;
32 import java.nio.file.*;
33 import java.nio.file.attribute.*;
34 import java.time.Instant;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.stream.Collectors;
40
41
42
43
44
45
46
47
48
49
50
51 public class FilesystemAsset implements StorageAsset, Comparable {
52
53 private final static Logger log = LoggerFactory.getLogger(FilesystemAsset.class);
54
55 private final Path basePath;
56 private final Path assetPath;
57 private final String relativePath;
58
59 public static final String DEFAULT_POSIX_FILE_PERMS = "rw-rw----";
60 public static final String DEFAULT_POSIX_DIR_PERMS = "rwxrwx---";
61
62 public static final Set<PosixFilePermission> DEFAULT_POSIX_FILE_PERMISSIONS;
63 public static final Set<PosixFilePermission> DEFAULT_POSIX_DIR_PERMISSIONS;
64
65 public static final AclEntryPermission[] DEFAULT_ACL_FILE_PERMISSIONS = new AclEntryPermission[]{
66 AclEntryPermission.DELETE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_ACL,
67 AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA
68 };
69
70 public static final AclEntryPermission[] DEFAULT_ACL_DIR_PERMISSIONS = new AclEntryPermission[]{
71 AclEntryPermission.ADD_FILE, AclEntryPermission.ADD_SUBDIRECTORY, AclEntryPermission.DELETE_CHILD,
72 AclEntryPermission.DELETE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_ACL,
73 AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA
74 };
75
76 static {
77
78 DEFAULT_POSIX_FILE_PERMISSIONS = PosixFilePermissions.fromString(DEFAULT_POSIX_FILE_PERMS);
79 DEFAULT_POSIX_DIR_PERMISSIONS = PosixFilePermissions.fromString(DEFAULT_POSIX_DIR_PERMS);
80 }
81
82 Set<PosixFilePermission> defaultPosixFilePermissions = DEFAULT_POSIX_FILE_PERMISSIONS;
83 Set<PosixFilePermission> defaultPosixDirectoryPermissions = DEFAULT_POSIX_DIR_PERMISSIONS;
84
85 List<AclEntry> defaultFileAcls;
86 List<AclEntry> defaultDirectoryAcls;
87
88 boolean supportsAcl = false;
89 boolean supportsPosix = false;
90 final boolean setPermissionsForNew;
91 final RepositoryStorage storage;
92
93 boolean directoryHint = false;
94
95 private static final OpenOption[] REPLACE_OPTIONS = new OpenOption[]{StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE};
96 private static final OpenOption[] APPEND_OPTIONS = new OpenOption[]{StandardOpenOption.APPEND};
97
98
99 FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath) {
100 this.assetPath = assetPath;
101 this.relativePath = normalizePath(path);
102 this.setPermissionsForNew=false;
103 this.basePath = basePath;
104 this.storage = storage;
105 init();
106 }
107
108
109
110
111
112
113
114
115 public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath) {
116 this.assetPath = assetPath;
117 this.relativePath = normalizePath(path);
118 this.setPermissionsForNew = false;
119 this.basePath = null;
120 this.storage = storage;
121
122 if ("".equals(path) || "/".equals(path)) {
123 this.directoryHint = true;
124 }
125 init();
126 }
127
128
129
130
131
132
133
134
135
136
137 public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath, boolean directory) {
138 this.assetPath = assetPath;
139 this.relativePath = normalizePath(path);
140 this.directoryHint = directory;
141 this.setPermissionsForNew = false;
142 this.basePath = basePath;
143 this.storage = storage;
144 init();
145 }
146
147
148
149
150
151
152
153
154
155
156 public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath, boolean directory, boolean setPermissionsForNew) {
157 this.assetPath = assetPath;
158 this.relativePath = normalizePath(path);
159 this.directoryHint = directory;
160 this.setPermissionsForNew = setPermissionsForNew;
161 this.basePath = basePath;
162 this.storage = storage;
163 init();
164 }
165
166 private String normalizePath(final String path) {
167 if (!path.startsWith("/")) {
168 return "/"+path;
169 } else {
170 String tmpPath = path;
171 while (tmpPath.startsWith("//")) {
172 tmpPath = tmpPath.substring(1);
173 }
174 return tmpPath;
175 }
176 }
177
178 private void init() {
179
180 if (setPermissionsForNew) {
181 try {
182 supportsAcl = Files.getFileStore(assetPath.getRoot()).supportsFileAttributeView(AclFileAttributeView.class);
183 } catch (IOException e) {
184 log.error("Could not check filesystem capabilities {}", e.getMessage());
185 }
186 try {
187 supportsPosix = Files.getFileStore(assetPath.getRoot()).supportsFileAttributeView(PosixFileAttributeView.class);
188 } catch (IOException e) {
189 log.error("Could not check filesystem capabilities {}", e.getMessage());
190 }
191
192 if (supportsAcl) {
193 AclFileAttributeView aclView = Files.getFileAttributeView(assetPath.getParent(), AclFileAttributeView.class);
194 UserPrincipal owner = null;
195 try {
196 owner = aclView.getOwner();
197 setDefaultFileAcls(processPermissions(owner, DEFAULT_ACL_FILE_PERMISSIONS));
198 setDefaultDirectoryAcls(processPermissions(owner, DEFAULT_ACL_DIR_PERMISSIONS));
199
200 } catch (IOException e) {
201 supportsAcl = false;
202 }
203
204
205 }
206 }
207 }
208
209 private List<AclEntry> processPermissions(UserPrincipal owner, AclEntryPermission[] defaultAclFilePermissions) {
210 AclEntry.Builder aclBuilder = AclEntry.newBuilder();
211 aclBuilder.setPermissions(defaultAclFilePermissions);
212 aclBuilder.setType(AclEntryType.ALLOW);
213 aclBuilder.setPrincipal(owner);
214 ArrayList<AclEntry> aclList = new ArrayList<>();
215 aclList.add(aclBuilder.build());
216 return aclList;
217 }
218
219
220 @Override
221 public RepositoryStorage getStorage( )
222 {
223 return storage;
224 }
225
226 @Override
227 public String getPath() {
228 return relativePath;
229 }
230
231 @Override
232 public String getName() {
233 return assetPath.getFileName().toString();
234 }
235
236 @Override
237 public Instant getModificationTime() {
238 try {
239 return Files.getLastModifiedTime(assetPath).toInstant();
240 } catch (IOException e) {
241 log.error("Could not read modification time of {}", assetPath);
242 return Instant.now();
243 }
244 }
245
246
247
248
249
250
251 @Override
252 public boolean isContainer() {
253 if (Files.exists(assetPath)) {
254 return Files.isDirectory(assetPath);
255 } else {
256 return directoryHint;
257 }
258 }
259
260
261
262
263
264
265
266 @Override
267 public List<StorageAsset> list() {
268 try {
269 return Files.list(assetPath).map(p -> new FilesystemAsset(storage, relativePath + "/" + p.getFileName().toString(), assetPath.resolve(p)))
270 .collect(Collectors.toList());
271 } catch (IOException e) {
272 return Collections.EMPTY_LIST;
273 }
274 }
275
276
277
278
279
280
281 @Override
282 public long getSize() {
283 try {
284 return Files.size(assetPath);
285 } catch (IOException e) {
286 return -1;
287 }
288 }
289
290
291
292
293
294
295
296
297 @Override
298 public InputStream getReadStream() throws IOException {
299 if (isContainer()) {
300 throw new IOException("Can not create input stream for container");
301 }
302 return Files.newInputStream(assetPath);
303 }
304
305 @Override
306 public ReadableByteChannel getReadChannel( ) throws IOException
307 {
308 return FileChannel.open( assetPath, StandardOpenOption.READ );
309 }
310
311 private OpenOption[] getOpenOptions(boolean replace) {
312 return replace ? REPLACE_OPTIONS : APPEND_OPTIONS;
313 }
314
315 @Override
316 public OutputStream getWriteStream( boolean replace) throws IOException {
317 OpenOption[] options = getOpenOptions( replace );
318 if (!Files.exists( assetPath )) {
319 create();
320 }
321 return Files.newOutputStream(assetPath, options);
322 }
323
324 @Override
325 public WritableByteChannel getWriteChannel( boolean replace ) throws IOException
326 {
327 OpenOption[] options = getOpenOptions( replace );
328 return FileChannel.open( assetPath, options );
329 }
330
331 @Override
332 public boolean replaceDataFromFile( Path newData) throws IOException {
333 final boolean createNew = !Files.exists(assetPath);
334 Path backup = null;
335 if (!createNew) {
336 backup = findBackupFile(assetPath);
337 }
338 try {
339 if (!createNew) {
340 Files.move(assetPath, backup);
341 }
342 Files.move(newData, assetPath, StandardCopyOption.REPLACE_EXISTING);
343 applyDefaultPermissions(assetPath);
344 return true;
345 } catch (IOException e) {
346 log.error("Could not overwrite file {}", assetPath);
347
348 if (backup != null && Files.exists(backup)) {
349 Files.move(backup, assetPath, StandardCopyOption.REPLACE_EXISTING);
350 }
351 throw e;
352 } finally {
353 if (backup != null) {
354 try {
355 Files.deleteIfExists(backup);
356 } catch (IOException e) {
357 log.error("Could not delete backup file {}", backup);
358 }
359 }
360 }
361
362 }
363
364 private void applyDefaultPermissions(Path filePath) {
365 try {
366 if (supportsPosix) {
367 Set<PosixFilePermission> perms;
368 if (Files.isDirectory(filePath)) {
369 perms = defaultPosixFilePermissions;
370 } else {
371 perms = defaultPosixDirectoryPermissions;
372 }
373 Files.setPosixFilePermissions(filePath, perms);
374 } else if (supportsAcl) {
375 List<AclEntry> perms;
376 if (Files.isDirectory(filePath)) {
377 perms = getDefaultDirectoryAcls();
378 } else {
379 perms = getDefaultFileAcls();
380 }
381 AclFileAttributeView aclAttr = Files.getFileAttributeView(filePath, AclFileAttributeView.class);
382 aclAttr.setAcl(perms);
383 }
384 } catch (IOException e) {
385 log.error("Could not set permissions for {}: {}", filePath, e.getMessage());
386 }
387 }
388
389 private Path findBackupFile(Path file) {
390 String ext = ".bak";
391 Path backupPath = file.getParent().resolve(file.getFileName().toString() + ext);
392 int idx = 0;
393 while (Files.exists(backupPath)) {
394 backupPath = file.getParent().resolve(file.getFileName().toString() + ext + idx++);
395 }
396 return backupPath;
397 }
398
399 @Override
400 public boolean exists() {
401 return Files.exists(assetPath);
402 }
403
404 @Override
405 public Path getFilePath() throws UnsupportedOperationException {
406 return assetPath;
407 }
408
409 @Override
410 public boolean isFileBased( )
411 {
412 return true;
413 }
414
415 @Override
416 public boolean hasParent( )
417 {
418 if (basePath!=null && assetPath.equals(basePath)) {
419 return false;
420 }
421 return assetPath.getParent()!=null;
422 }
423
424 @Override
425 public StorageAsset getParent( )
426 {
427 Path parentPath;
428 if (basePath!=null && assetPath.equals( basePath )) {
429 parentPath=null;
430 } else
431 {
432 parentPath = assetPath.getParent( );
433 }
434 String relativeParent = StringUtils.substringBeforeLast( relativePath,"/");
435 if (parentPath!=null) {
436 return new FilesystemAsset(storage, relativeParent, parentPath, basePath, true, setPermissionsForNew );
437 } else {
438 return null;
439 }
440 }
441
442 @Override
443 public StorageAsset resolve(String toPath) {
444 return storage.getAsset(this.getPath()+"/"+toPath);
445 }
446
447
448 public void setDefaultFileAcls(List<AclEntry> acl) {
449 defaultFileAcls = acl;
450 }
451
452 public List<AclEntry> getDefaultFileAcls() {
453 return defaultFileAcls;
454 }
455
456 public void setDefaultPosixFilePermissions(Set<PosixFilePermission> perms) {
457 defaultPosixFilePermissions = perms;
458 }
459
460 public Set<PosixFilePermission> getDefaultPosixFilePermissions() {
461 return defaultPosixFilePermissions;
462 }
463
464 public void setDefaultDirectoryAcls(List<AclEntry> acl) {
465 defaultDirectoryAcls = acl;
466 }
467
468 public List<AclEntry> getDefaultDirectoryAcls() {
469 return defaultDirectoryAcls;
470 }
471
472 public void setDefaultPosixDirectoryPermissions(Set<PosixFilePermission> perms) {
473 defaultPosixDirectoryPermissions = perms;
474 }
475
476 public Set<PosixFilePermission> getDefaultPosixDirectoryPermissions() {
477 return defaultPosixDirectoryPermissions;
478 }
479
480 @Override
481 public void create() throws IOException {
482 if (!Files.exists(assetPath)) {
483 if (directoryHint) {
484 Files.createDirectories(assetPath);
485 } else {
486 if (!Files.exists( assetPath.getParent() )) {
487 Files.createDirectories( assetPath.getParent( ) );
488 }
489 Files.createFile(assetPath);
490 }
491 if (setPermissionsForNew) {
492 applyDefaultPermissions(assetPath);
493 }
494 }
495 }
496
497 @Override
498 public String toString() {
499 return relativePath+":"+assetPath;
500 }
501
502 @Override
503 public int compareTo(Object o) {
504 if (o instanceof FilesystemAsset) {
505 if (this.getPath()!=null) {
506 return this.getPath().compareTo(((FilesystemAsset) o).getPath());
507 } else {
508 return 0;
509 }
510 }
511 return 0;
512 }
513 }