1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.util;
45
46 import static java.nio.charset.StandardCharsets.UTF_8;
47
48 import java.io.BufferedReader;
49 import java.io.ByteArrayInputStream;
50 import java.io.Closeable;
51 import java.io.File;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.InputStreamReader;
55 import java.io.OutputStream;
56 import java.io.PrintStream;
57 import java.nio.charset.Charset;
58 import java.nio.file.Files;
59 import java.nio.file.Path;
60 import java.nio.file.attribute.BasicFileAttributes;
61 import java.security.AccessController;
62 import java.security.PrivilegedAction;
63 import java.text.MessageFormat;
64 import java.util.Arrays;
65 import java.util.HashMap;
66 import java.util.Map;
67 import java.util.Objects;
68 import java.util.Optional;
69 import java.util.concurrent.ExecutorService;
70 import java.util.concurrent.Executors;
71 import java.util.concurrent.TimeUnit;
72 import java.util.concurrent.atomic.AtomicBoolean;
73 import java.util.concurrent.atomic.AtomicReference;
74
75 import org.eclipse.jgit.annotations.Nullable;
76 import org.eclipse.jgit.api.errors.JGitInternalException;
77 import org.eclipse.jgit.errors.CommandFailedException;
78 import org.eclipse.jgit.internal.JGitText;
79 import org.eclipse.jgit.lib.Constants;
80 import org.eclipse.jgit.lib.Repository;
81 import org.eclipse.jgit.treewalk.FileTreeIterator.FileEntry;
82 import org.eclipse.jgit.treewalk.FileTreeIterator.FileModeStrategy;
83 import org.eclipse.jgit.treewalk.WorkingTreeIterator.Entry;
84 import org.eclipse.jgit.util.ProcessResult.Status;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87
88
89
90
91 public abstract class FS {
92 private static final Logger LOG = LoggerFactory.getLogger(FS.class);
93
94
95
96
97
98
99
100 protected static final Entry[] NO_ENTRIES = {};
101
102
103
104
105
106
107
108 public static class FSFactory {
109
110
111
112 protected FSFactory() {
113
114 }
115
116
117
118
119
120
121
122 public FS detect(Boolean cygwinUsed) {
123 if (SystemReader.getInstance().isWindows()) {
124 if (cygwinUsed == null)
125 cygwinUsed = Boolean.valueOf(FS_Win32_Cygwin.isCygwin());
126 if (cygwinUsed.booleanValue())
127 return new FS_Win32_Cygwin();
128 else
129 return new FS_Win32();
130 } else {
131 return new FS_POSIX();
132 }
133 }
134 }
135
136
137
138
139
140
141
142 public static class ExecutionResult {
143 private TemporaryBuffer stdout;
144
145 private TemporaryBuffer stderr;
146
147 private int rc;
148
149
150
151
152
153
154 public ExecutionResult(TemporaryBuffer./../org/eclipse/jgit/util/TemporaryBuffer.html#TemporaryBuffer">TemporaryBuffer stdout, TemporaryBuffer stderr,
155 int rc) {
156 this.stdout = stdout;
157 this.stderr = stderr;
158 this.rc = rc;
159 }
160
161
162
163
164 public TemporaryBuffer getStdout() {
165 return stdout;
166 }
167
168
169
170
171 public TemporaryBuffer getStderr() {
172 return stderr;
173 }
174
175
176
177
178 public int getRc() {
179 return rc;
180 }
181 }
182
183
184 public static final FS DETECTED = detect();
185
186 private volatile static FSFactory factory;
187
188
189
190
191
192
193 public static FS detect() {
194 return detect(null);
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 public static FS detect(Boolean cygwinUsed) {
218 if (factory == null) {
219 factory = new FS.FSFactory();
220 }
221 return factory.detect(cygwinUsed);
222 }
223
224 private volatile Holder<File> userHome;
225
226 private volatile Holder<File> gitSystemConfig;
227
228
229
230
231 protected FS() {
232
233 }
234
235
236
237
238
239
240
241 protected FSme="FS" href="../../../../org/eclipse/jgit/util/FS.html#FS">FS(FS src) {
242 userHome = src.userHome;
243 gitSystemConfig = src.gitSystemConfig;
244 }
245
246
247
248
249
250
251 public abstract FS newInstance();
252
253
254
255
256
257
258
259 public abstract boolean supportsExecute();
260
261
262
263
264
265
266
267
268
269
270
271
272 public boolean supportsAtomicCreateNewFile() {
273 return true;
274 }
275
276
277
278
279
280
281
282
283 public boolean supportsSymlinks() {
284 return false;
285 }
286
287
288
289
290
291
292 public abstract boolean isCaseSensitive();
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 public abstract boolean canExecute(File f);
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323 public abstract boolean setExecute(File f, boolean canExec);
324
325
326
327
328
329
330
331
332
333
334
335
336 public long lastModified(File f) throws IOException {
337 return FileUtils.lastModified(f);
338 }
339
340
341
342
343
344
345
346
347
348
349
350
351 public void setLastModified(File f, long time) throws IOException {
352 FileUtils.setLastModified(f, time);
353 }
354
355
356
357
358
359
360
361
362
363
364
365 public long length(File path) throws IOException {
366 return FileUtils.getLength(path);
367 }
368
369
370
371
372
373
374
375
376
377
378 public void delete(File f) throws IOException {
379 FileUtils.delete(f);
380 }
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 public File resolve(File dir, String name) {
401 final File abspn = new File(name);
402 if (abspn.isAbsolute())
403 return abspn;
404 return new File(dir, name);
405 }
406
407
408
409
410
411
412
413
414
415
416
417
418 public File userHome() {
419 Holder<File> p = userHome;
420 if (p == null) {
421 p = new Holder<>(userHomeImpl());
422 userHome = p;
423 }
424 return p.value;
425 }
426
427
428
429
430
431
432
433
434
435 public FS setUserHome(File path) {
436 userHome = new Holder<>(path);
437 return this;
438 }
439
440
441
442
443
444
445 public abstract boolean retryFailedLockFileCommit();
446
447
448
449
450
451
452
453
454
455
456 public BasicFileAttributes fileAttributes(File file) throws IOException {
457 return FileUtils.fileAttributes(file);
458 }
459
460
461
462
463
464
465 protected File userHomeImpl() {
466 final String home = AccessController
467 .doPrivileged(new PrivilegedAction<String>() {
468 @Override
469 public String run() {
470 return System.getProperty("user.home");
471 }
472 });
473 if (home == null || home.length() == 0)
474 return null;
475 return new File(home).getAbsoluteFile();
476 }
477
478
479
480
481
482
483
484
485
486
487
488
489 protected static File searchPath(String path, String... lookFor) {
490 if (path == null)
491 return null;
492
493 for (String p : path.split(File.pathSeparator)) {
494 for (String command : lookFor) {
495 final File e = new File(p, command);
496 if (e.isFile())
497 return e.getAbsoluteFile();
498 }
499 }
500 return null;
501 }
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517 @Nullable
518 protected static String readPipe(File dir, String[] command,
519 String encoding) throws CommandFailedException {
520 return readPipe(dir, command, encoding, null);
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541 @Nullable
542 protected static String readPipe(File dir, String[] command,
543 String encoding, Map<String, String> env)
544 throws CommandFailedException {
545 final boolean debug = LOG.isDebugEnabled();
546 try {
547 if (debug) {
548 LOG.debug("readpipe " + Arrays.asList(command) + ","
549 + dir);
550 }
551 ProcessBuilder pb = new ProcessBuilder(command);
552 pb.directory(dir);
553 if (env != null) {
554 pb.environment().putAll(env);
555 }
556 Process p;
557 try {
558 p = pb.start();
559 } catch (IOException e) {
560
561 throw new CommandFailedException(-1, e.getMessage(), e);
562 }
563 p.getOutputStream().close();
564 GobblerThread gobbler = new GobblerThread(p, command, dir);
565 gobbler.start();
566 String r = null;
567 try (BufferedReader lineRead = new BufferedReader(
568 new InputStreamReader(p.getInputStream(), encoding))) {
569 r = lineRead.readLine();
570 if (debug) {
571 LOG.debug("readpipe may return '" + r + "'");
572 LOG.debug("remaining output:\n");
573 String l;
574 while ((l = lineRead.readLine()) != null) {
575 LOG.debug(l);
576 }
577 }
578 }
579
580 for (;;) {
581 try {
582 int rc = p.waitFor();
583 gobbler.join();
584 if (rc == 0 && !gobbler.fail.get()) {
585 return r;
586 } else {
587 if (debug) {
588 LOG.debug("readpipe rc=" + rc);
589 }
590 throw new CommandFailedException(rc,
591 gobbler.errorMessage.get(),
592 gobbler.exception.get());
593 }
594 } catch (InterruptedException ie) {
595
596 }
597 }
598 } catch (IOException e) {
599 LOG.error("Caught exception in FS.readPipe()", e);
600 }
601 if (debug) {
602 LOG.debug("readpipe returns null");
603 }
604 return null;
605 }
606
607 private static class GobblerThread extends Thread {
608
609
610 private static final int PROCESS_EXIT_TIMEOUT = 5;
611
612 private final Process p;
613 private final String desc;
614 private final String dir;
615 final AtomicBoolean fail = new AtomicBoolean();
616 final AtomicReference<String> errorMessage = new AtomicReference<>();
617 final AtomicReference<Throwable> exception = new AtomicReference<>();
618
619 GobblerThread(Process p, String[] command, File dir) {
620 this.p = p;
621 this.desc = Arrays.toString(command);
622 this.dir = Objects.toString(dir);
623 }
624
625 @Override
626 public void run() {
627 StringBuilder err = new StringBuilder();
628 try (InputStream is = p.getErrorStream()) {
629 int ch;
630 while ((ch = is.read()) != -1) {
631 err.append((char) ch);
632 }
633 } catch (IOException e) {
634 if (waitForProcessCompletion(e) && p.exitValue() != 0) {
635 setError(e, e.getMessage(), p.exitValue());
636 fail.set(true);
637 } else {
638
639
640 }
641 } finally {
642 if (waitForProcessCompletion(null) && err.length() > 0) {
643 setError(null, err.toString(), p.exitValue());
644 if (p.exitValue() != 0) {
645 fail.set(true);
646 }
647 }
648 }
649 }
650
651 @SuppressWarnings("boxing")
652 private boolean waitForProcessCompletion(IOException originalError) {
653 try {
654 if (!p.waitFor(PROCESS_EXIT_TIMEOUT, TimeUnit.SECONDS)) {
655 setError(originalError, MessageFormat.format(
656 JGitText.get().commandClosedStderrButDidntExit,
657 desc, PROCESS_EXIT_TIMEOUT), -1);
658 fail.set(true);
659 return false;
660 }
661 } catch (InterruptedException e) {
662 setError(originalError, MessageFormat.format(
663 JGitText.get().threadInterruptedWhileRunning, desc), -1);
664 fail.set(true);
665 return false;
666 }
667 return true;
668 }
669
670 private void setError(IOException e, String message, int exitCode) {
671 exception.set(e);
672 errorMessage.set(MessageFormat.format(
673 JGitText.get().exceptionCaughtDuringExecutionOfCommand,
674 desc, dir, Integer.valueOf(exitCode), message));
675 }
676 }
677
678
679
680
681
682
683
684
685 protected abstract File discoverGitExe();
686
687
688
689
690
691
692
693
694 protected File discoverGitSystemConfig() {
695 File gitExe = discoverGitExe();
696 if (gitExe == null) {
697 return null;
698 }
699
700
701 String v;
702 try {
703 v = readPipe(gitExe.getParentFile(),
704 new String[] { "git", "--version" },
705 Charset.defaultCharset().name());
706 } catch (CommandFailedException e) {
707 LOG.warn(e.getMessage());
708 return null;
709 }
710 if (StringUtils.isEmptyOrNull(v)
711 || (v != null && v.startsWith("jgit"))) {
712 return null;
713 }
714
715
716
717 Map<String, String> env = new HashMap<>();
718 env.put("GIT_EDITOR", "echo");
719
720 String w;
721 try {
722 w = readPipe(gitExe.getParentFile(),
723 new String[] { "git", "config", "--system", "--edit" },
724 Charset.defaultCharset().name(), env);
725 } catch (CommandFailedException e) {
726 LOG.warn(e.getMessage());
727 return null;
728 }
729 if (StringUtils.isEmptyOrNull(w)) {
730 return null;
731 }
732
733 return new File(w);
734 }
735
736
737
738
739
740
741
742
743 public File getGitSystemConfig() {
744 if (gitSystemConfig == null) {
745 gitSystemConfig = new Holder<>(discoverGitSystemConfig());
746 }
747 return gitSystemConfig.value;
748 }
749
750
751
752
753
754
755
756
757
758 public FS setGitSystemConfig(File configFile) {
759 gitSystemConfig = new Holder<>(configFile);
760 return this;
761 }
762
763
764
765
766
767
768
769
770
771
772 protected static File resolveGrandparentFile(File grandchild) {
773 if (grandchild != null) {
774 File parent = grandchild.getParentFile();
775 if (parent != null)
776 return parent.getParentFile();
777 }
778 return null;
779 }
780
781
782
783
784
785
786
787
788
789
790 public String readSymLink(File path) throws IOException {
791 return FileUtils.readSymLink(path);
792 }
793
794
795
796
797
798
799
800
801
802
803 public boolean isSymLink(File path) throws IOException {
804 return FileUtils.isSymlink(path);
805 }
806
807
808
809
810
811
812
813
814
815
816 public boolean exists(File path) {
817 return FileUtils.exists(path);
818 }
819
820
821
822
823
824
825
826
827
828
829 public boolean isDirectory(File path) {
830 return FileUtils.isDirectory(path);
831 }
832
833
834
835
836
837
838
839
840
841
842 public boolean isFile(File path) {
843 return FileUtils.isFile(path);
844 }
845
846
847
848
849
850
851
852
853
854
855
856
857 public boolean isHidden(File path) throws IOException {
858 return FileUtils.isHidden(path);
859 }
860
861
862
863
864
865
866
867
868
869
870
871 public void setHidden(File path, boolean hidden) throws IOException {
872 FileUtils.setHidden(path, hidden);
873 }
874
875
876
877
878
879
880
881
882
883
884
885 public void createSymLink(File path, String target) throws IOException {
886 FileUtils.createSymLink(path, target);
887 }
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902 @Deprecated
903 public boolean createNewFile(File path) throws IOException {
904 return path.createNewFile();
905 }
906
907
908
909
910
911
912
913
914
915
916 public static class LockToken implements Closeable {
917 private boolean isCreated;
918
919 private Optional<Path> link;
920
921 LockToken(boolean isCreated, Optional<Path> link) {
922 this.isCreated = isCreated;
923 this.link = link;
924 }
925
926
927
928
929 public boolean isCreated() {
930 return isCreated;
931 }
932
933 @Override
934 public void close() {
935 if (!link.isPresent()) {
936 return;
937 }
938 Path p = link.get();
939 if (!Files.exists(p)) {
940 return;
941 }
942 try {
943 Files.delete(p);
944 } catch (IOException e) {
945 LOG.error(MessageFormat
946 .format(JGitText.get().closeLockTokenFailed, this), e);
947 }
948 }
949
950 @Override
951 public String toString() {
952 return "LockToken [lockCreated=" + isCreated +
953 ", link="
954 + (link.isPresent() ? link.get().getFileName() + "]"
955 : "<null>]");
956 }
957 }
958
959
960
961
962
963
964
965
966
967
968
969
970
971 public LockToken createNewFileAtomic(File path) throws IOException {
972 return new LockToken(path.createNewFile(), Optional.empty());
973 }
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989 public String relativize(String base, String other) {
990 return FileUtils.relativizePath(base, other, File.separator, this.isCaseSensitive());
991 }
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004 public Entry[] list(File directory, FileModeStrategy fileModeStrategy) {
1005 final File[] all = directory.listFiles();
1006 if (all == null) {
1007 return NO_ENTRIES;
1008 }
1009 final Entry[] result = new Entry[all.length];
1010 for (int i = 0; i < result.length; i++) {
1011 result[i] = new FileEntry(all[i], this, fileModeStrategy);
1012 }
1013 return result;
1014 }
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038 public ProcessResult runHookIfPresent(Repository repository,
1039 final String hookName,
1040 String[] args) throws JGitInternalException {
1041 return runHookIfPresent(repository, hookName, args, System.out, System.err,
1042 null);
1043 }
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 public ProcessResult runHookIfPresent(Repository repository,
1074 final String hookName,
1075 String[] args, PrintStream outRedirect, PrintStream errRedirect,
1076 String stdinArgs) throws JGitInternalException {
1077 return new ProcessResult(Status.NOT_SUPPORTED);
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109 protected ProcessResult internalRunHookIfPresent(Repository repository,
1110 final String hookName, String[] args, PrintStream outRedirect,
1111 PrintStream errRedirect, String stdinArgs)
1112 throws JGitInternalException {
1113 final File hookFile = findHook(repository, hookName);
1114 if (hookFile == null)
1115 return new ProcessResult(Status.NOT_PRESENT);
1116
1117 final String hookPath = hookFile.getAbsolutePath();
1118 final File runDirectory;
1119 if (repository.isBare())
1120 runDirectory = repository.getDirectory();
1121 else
1122 runDirectory = repository.getWorkTree();
1123 final String cmd = relativize(runDirectory.getAbsolutePath(),
1124 hookPath);
1125 ProcessBuilder hookProcess = runInShell(cmd, args);
1126 hookProcess.directory(runDirectory);
1127 Map<String, String> environment = hookProcess.environment();
1128 environment.put(Constants.GIT_DIR_KEY,
1129 repository.getDirectory().getAbsolutePath());
1130 if (!repository.isBare()) {
1131 environment.put(Constants.GIT_WORK_TREE_KEY,
1132 repository.getWorkTree().getAbsolutePath());
1133 }
1134 try {
1135 return new ProcessResult(runProcess(hookProcess, outRedirect,
1136 errRedirect, stdinArgs), Status.OK);
1137 } catch (IOException e) {
1138 throw new JGitInternalException(MessageFormat.format(
1139 JGitText.get().exceptionCaughtDuringExecutionOfHook,
1140 hookName), e);
1141 } catch (InterruptedException e) {
1142 throw new JGitInternalException(MessageFormat.format(
1143 JGitText.get().exceptionHookExecutionInterrupted,
1144 hookName), e);
1145 }
1146 }
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160 public File findHook(Repository repository, String hookName) {
1161 File gitDir = repository.getDirectory();
1162 if (gitDir == null)
1163 return null;
1164 final File hookFile = new File(new File(gitDir,
1165 Constants.HOOKS), hookName);
1166 return hookFile.isFile() ? hookFile : null;
1167 }
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194 public int runProcess(ProcessBuilder processBuilder,
1195 OutputStream outRedirect, OutputStream errRedirect, String stdinArgs)
1196 throws IOException, InterruptedException {
1197 InputStream in = (stdinArgs == null) ? null : new ByteArrayInputStream(
1198 stdinArgs.getBytes(UTF_8));
1199 return runProcess(processBuilder, outRedirect, errRedirect, in);
1200 }
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230 public int runProcess(ProcessBuilder processBuilder,
1231 OutputStream outRedirect, OutputStream errRedirect,
1232 InputStream inRedirect) throws IOException,
1233 InterruptedException {
1234 final ExecutorService executor = Executors.newFixedThreadPool(2);
1235 Process process = null;
1236
1237
1238 IOException ioException = null;
1239 try {
1240 process = processBuilder.start();
1241 executor.execute(
1242 new StreamGobbler(process.getErrorStream(), errRedirect));
1243 executor.execute(
1244 new StreamGobbler(process.getInputStream(), outRedirect));
1245 @SuppressWarnings("resource")
1246 OutputStream outputStream = process.getOutputStream();
1247 try {
1248 if (inRedirect != null) {
1249 new StreamGobbler(inRedirect, outputStream).copy();
1250 }
1251 } finally {
1252 try {
1253 outputStream.close();
1254 } catch (IOException e) {
1255
1256
1257
1258
1259
1260
1261 }
1262 }
1263 return process.waitFor();
1264 } catch (IOException e) {
1265 ioException = e;
1266 } finally {
1267 shutdownAndAwaitTermination(executor);
1268 if (process != null) {
1269 try {
1270 process.waitFor();
1271 } catch (InterruptedException e) {
1272
1273
1274
1275
1276 Thread.interrupted();
1277 }
1278
1279
1280
1281 if (inRedirect != null) {
1282 inRedirect.close();
1283 }
1284 try {
1285 process.getErrorStream().close();
1286 } catch (IOException e) {
1287 ioException = ioException != null ? ioException : e;
1288 }
1289 try {
1290 process.getInputStream().close();
1291 } catch (IOException e) {
1292 ioException = ioException != null ? ioException : e;
1293 }
1294 try {
1295 process.getOutputStream().close();
1296 } catch (IOException e) {
1297 ioException = ioException != null ? ioException : e;
1298 }
1299 process.destroy();
1300 }
1301 }
1302
1303 throw ioException;
1304 }
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319 private static boolean shutdownAndAwaitTermination(ExecutorService pool) {
1320 boolean hasShutdown = true;
1321 pool.shutdown();
1322 try {
1323
1324 if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
1325 pool.shutdownNow();
1326
1327 if (!pool.awaitTermination(60, TimeUnit.SECONDS))
1328 hasShutdown = false;
1329 }
1330 } catch (InterruptedException ie) {
1331
1332 pool.shutdownNow();
1333
1334 Thread.currentThread().interrupt();
1335 hasShutdown = false;
1336 }
1337 return hasShutdown;
1338 }
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352 public abstract ProcessBuilder runInShell(String cmd, String[] args);
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366 public ExecutionResult execute(ProcessBuilder pb, InputStream in)
1367 throws IOException, InterruptedException {
1368 try (TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null);
1369 TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024,
1370 1024 * 1024)) {
1371 int rc = runProcess(pb, stdout, stderr, in);
1372 return new ExecutionResult(stdout, stderr, rc);
1373 }
1374 }
1375
1376 private static class Holder<V> {
1377 final V value;
1378
1379 Holder(V value) {
1380 this.value = value;
1381 }
1382 }
1383
1384
1385
1386
1387
1388
1389 public static class Attributes {
1390
1391
1392
1393
1394 public boolean isDirectory() {
1395 return isDirectory;
1396 }
1397
1398
1399
1400
1401 public boolean isExecutable() {
1402 return isExecutable;
1403 }
1404
1405
1406
1407
1408 public boolean isSymbolicLink() {
1409 return isSymbolicLink;
1410 }
1411
1412
1413
1414
1415 public boolean isRegularFile() {
1416 return isRegularFile;
1417 }
1418
1419
1420
1421
1422 public long getCreationTime() {
1423 return creationTime;
1424 }
1425
1426
1427
1428
1429
1430 public long getLastModifiedTime() {
1431 return lastModifiedTime;
1432 }
1433
1434 private final boolean isDirectory;
1435
1436 private final boolean isSymbolicLink;
1437
1438 private final boolean isRegularFile;
1439
1440 private final long creationTime;
1441
1442 private final long lastModifiedTime;
1443
1444 private final boolean isExecutable;
1445
1446 private final File file;
1447
1448 private final boolean exists;
1449
1450
1451
1452
1453 protected long length = -1;
1454
1455 final FS fs;
1456
1457 Attributes(FS fs, File file, boolean exists, boolean isDirectory,
1458 boolean isExecutable, boolean isSymbolicLink,
1459 boolean isRegularFile, long creationTime,
1460 long lastModifiedTime, long length) {
1461 this.fs = fs;
1462 this.file = file;
1463 this.exists = exists;
1464 this.isDirectory = isDirectory;
1465 this.isExecutable = isExecutable;
1466 this.isSymbolicLink = isSymbolicLink;
1467 this.isRegularFile = isRegularFile;
1468 this.creationTime = creationTime;
1469 this.lastModifiedTime = lastModifiedTime;
1470 this.length = length;
1471 }
1472
1473
1474
1475
1476
1477
1478
1479
1480 public Attributes(File path, FS fs) {
1481 this(fs, path, false, false, false, false, false, 0L, 0L, 0L);
1482 }
1483
1484
1485
1486
1487 public long getLength() {
1488 if (length == -1)
1489 return length = file.length();
1490 return length;
1491 }
1492
1493
1494
1495
1496 public String getName() {
1497 return file.getName();
1498 }
1499
1500
1501
1502
1503 public File getFile() {
1504 return file;
1505 }
1506
1507 boolean exists() {
1508 return exists;
1509 }
1510 }
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520 public Attributes getAttributes(File path) {
1521 boolean isDirectory = isDirectory(path);
1522 boolean isFile = !isDirectory && path.isFile();
1523 assert path.exists() == isDirectory || isFile;
1524 boolean exists = isDirectory || isFile;
1525 boolean canExecute = exists && !isDirectory && canExecute(path);
1526 boolean isSymlink = false;
1527 long lastModified = exists ? path.lastModified() : 0L;
1528 long createTime = 0L;
1529 return new Attributes(this, path, exists, isDirectory, canExecute,
1530 isSymlink, isFile, createTime, lastModified, -1);
1531 }
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541 public File normalize(File file) {
1542 return file;
1543 }
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553 public String normalize(String name) {
1554 return name;
1555 }
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569 private static class StreamGobbler implements Runnable {
1570 private InputStream in;
1571
1572 private OutputStream out;
1573
1574 public StreamGobbler(InputStream stream, OutputStream output) {
1575 this.in = stream;
1576 this.out = output;
1577 }
1578
1579 @Override
1580 public void run() {
1581 try {
1582 copy();
1583 } catch (IOException e) {
1584
1585 }
1586 }
1587
1588 void copy() throws IOException {
1589 boolean writeFailure = false;
1590 byte buffer[] = new byte[4096];
1591 int readBytes;
1592 while ((readBytes = in.read(buffer)) != -1) {
1593
1594
1595
1596 if (!writeFailure && out != null) {
1597 try {
1598 out.write(buffer, 0, readBytes);
1599 out.flush();
1600 } catch (IOException e) {
1601 writeFailure = true;
1602 }
1603 }
1604 }
1605 }
1606 }
1607 }