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