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
45
46 package org.eclipse.jgit.util;
47
48 import java.io.File;
49 import java.io.IOException;
50 import java.nio.channels.FileLock;
51 import java.nio.file.AtomicMoveNotSupportedException;
52 import java.nio.file.CopyOption;
53 import java.nio.file.Files;
54 import java.nio.file.LinkOption;
55 import java.nio.file.Path;
56 import java.nio.file.StandardCopyOption;
57 import java.nio.file.attribute.BasicFileAttributeView;
58 import java.nio.file.attribute.BasicFileAttributes;
59 import java.nio.file.attribute.FileTime;
60 import java.nio.file.attribute.PosixFileAttributeView;
61 import java.nio.file.attribute.PosixFileAttributes;
62 import java.nio.file.attribute.PosixFilePermission;
63 import java.text.MessageFormat;
64 import java.text.Normalizer;
65 import java.text.Normalizer.Form;
66 import java.util.ArrayList;
67 import java.util.List;
68 import java.util.regex.Pattern;
69
70 import org.eclipse.jgit.internal.JGitText;
71 import org.eclipse.jgit.lib.Constants;
72 import org.eclipse.jgit.util.FS.Attributes;
73
74
75
76
77 public class FileUtils {
78
79
80
81
82 public static final int NONE = 0;
83
84
85
86
87 public static final int RECURSIVE = 1;
88
89
90
91
92 public static final int RETRY = 2;
93
94
95
96
97 public static final int SKIP_MISSING = 4;
98
99
100
101
102
103 public static final int IGNORE_ERRORS = 8;
104
105
106
107
108
109
110
111 public static final int EMPTY_DIRECTORIES_ONLY = 16;
112
113
114
115
116
117
118
119
120
121
122
123
124 public static void delete(final File f) throws IOException {
125 delete(f, NONE);
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public static void delete(final File f, int options) throws IOException {
146 FS fs = FS.DETECTED;
147 if ((options & SKIP_MISSING) != 0 && !fs.exists(f))
148 return;
149
150 if ((options & RECURSIVE) != 0 && fs.isDirectory(f)) {
151 final File[] items = f.listFiles();
152 if (items != null) {
153 List<File> files = new ArrayList<File>();
154 List<File> dirs = new ArrayList<File>();
155 for (File c : items)
156 if (c.isFile())
157 files.add(c);
158 else
159 dirs.add(c);
160
161
162
163 for (File file : files)
164 delete(file, options);
165 for (File d : dirs)
166 delete(d, options);
167 }
168 }
169
170 boolean delete = false;
171 if ((options & EMPTY_DIRECTORIES_ONLY) != 0) {
172 if (f.isDirectory()) {
173 delete = true;
174 } else {
175 if ((options & IGNORE_ERRORS) == 0)
176 throw new IOException(MessageFormat.format(
177 JGitText.get().deleteFileFailed,
178 f.getAbsolutePath()));
179 }
180 } else {
181 delete = true;
182 }
183
184 if (delete && !f.delete()) {
185 if ((options & RETRY) != 0 && fs.exists(f)) {
186 for (int i = 1; i < 10; i++) {
187 try {
188 Thread.sleep(100);
189 } catch (InterruptedException e) {
190
191 }
192 if (f.delete())
193 return;
194 }
195 }
196 if ((options & IGNORE_ERRORS) == 0)
197 throw new IOException(MessageFormat.format(
198 JGitText.get().deleteFileFailed, f.getAbsolutePath()));
199 }
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 public static void rename(final File src, final File dst)
225 throws IOException {
226 rename(src, dst, StandardCopyOption.REPLACE_EXISTING);
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 public static void rename(final File src, final File dst,
256 CopyOption... options)
257 throws AtomicMoveNotSupportedException, IOException {
258 int attempts = FS.DETECTED.retryFailedLockFileCommit() ? 10 : 1;
259 while (--attempts >= 0) {
260 try {
261 Files.move(src.toPath(), dst.toPath(), options);
262 return;
263 } catch (AtomicMoveNotSupportedException e) {
264 throw e;
265 } catch (IOException e) {
266 try {
267 if (!dst.delete()) {
268 delete(dst, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
269 }
270
271 Files.move(src.toPath(), dst.toPath(), options);
272 return;
273 } catch (IOException e2) {
274
275 }
276 }
277 try {
278 Thread.sleep(100);
279 } catch (InterruptedException e) {
280 throw new IOException(
281 MessageFormat.format(JGitText.get().renameFileFailed,
282 src.getAbsolutePath(), dst.getAbsolutePath()));
283 }
284 }
285 throw new IOException(
286 MessageFormat.format(JGitText.get().renameFileFailed,
287 src.getAbsolutePath(), dst.getAbsolutePath()));
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301 public static void mkdir(final File d)
302 throws IOException {
303 mkdir(d, false);
304 }
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320 public static void mkdir(final File d, boolean skipExisting)
321 throws IOException {
322 if (!d.mkdir()) {
323 if (skipExisting && d.isDirectory())
324 return;
325 throw new IOException(MessageFormat.format(
326 JGitText.get().mkDirFailed, d.getAbsolutePath()));
327 }
328 }
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 public static void mkdirs(final File d) throws IOException {
345 mkdirs(d, false);
346 }
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365 public static void mkdirs(final File d, boolean skipExisting)
366 throws IOException {
367 if (!d.mkdirs()) {
368 if (skipExisting && d.isDirectory())
369 return;
370 throw new IOException(MessageFormat.format(
371 JGitText.get().mkDirsFailed, d.getAbsolutePath()));
372 }
373 }
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391 public static void createNewFile(File f) throws IOException {
392 if (!f.createNewFile())
393 throw new IOException(MessageFormat.format(
394 JGitText.get().createNewFileFailed, f));
395 }
396
397
398
399
400
401
402
403
404
405
406
407
408 public static Path createSymLink(File path, String target)
409 throws IOException {
410 Path nioPath = path.toPath();
411 if (Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS)) {
412 BasicFileAttributes attrs = Files.readAttributes(nioPath,
413 BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
414 if (attrs.isRegularFile() || attrs.isSymbolicLink()) {
415 delete(path);
416 } else {
417 delete(path, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
418 }
419 }
420 if (SystemReader.getInstance().isWindows()) {
421 target = target.replace('/', '\\');
422 }
423 Path nioTarget = new File(target).toPath();
424 return Files.createSymbolicLink(nioPath, nioTarget);
425 }
426
427
428
429
430
431
432
433 public static String readSymLink(File path) throws IOException {
434 Path nioPath = path.toPath();
435 Path target = Files.readSymbolicLink(nioPath);
436 String targetString = target.toString();
437 if (SystemReader.getInstance().isWindows()) {
438 targetString = targetString.replace('\\', '/');
439 } else if (SystemReader.getInstance().isMacOS()) {
440 targetString = Normalizer.normalize(targetString, Form.NFC);
441 }
442 return targetString;
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456 public static File createTempDir(String prefix, String suffix, File dir)
457 throws IOException {
458 final int RETRIES = 1;
459 for (int i = 0; i < RETRIES; i++) {
460 File tmp = File.createTempFile(prefix, suffix, dir);
461 if (!tmp.delete())
462 continue;
463 if (!tmp.mkdir())
464 continue;
465 return tmp;
466 }
467 throw new IOException(JGitText.get().cannotCreateTempDir);
468 }
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500 public static String relativize(String base, String other) {
501 if (base.equals(other))
502 return "";
503
504 final boolean ignoreCase = !FS.DETECTED.isCaseSensitive();
505 final String[] baseSegments = base.split(Pattern.quote(File.separator));
506 final String[] otherSegments = other.split(Pattern
507 .quote(File.separator));
508
509 int commonPrefix = 0;
510 while (commonPrefix < baseSegments.length
511 && commonPrefix < otherSegments.length) {
512 if (ignoreCase
513 && baseSegments[commonPrefix]
514 .equalsIgnoreCase(otherSegments[commonPrefix]))
515 commonPrefix++;
516 else if (!ignoreCase
517 && baseSegments[commonPrefix]
518 .equals(otherSegments[commonPrefix]))
519 commonPrefix++;
520 else
521 break;
522 }
523
524 final StringBuilder builder = new StringBuilder();
525 for (int i = commonPrefix; i < baseSegments.length; i++)
526 builder.append("..").append(File.separator);
527 for (int i = commonPrefix; i < otherSegments.length; i++) {
528 builder.append(otherSegments[i]);
529 if (i < otherSegments.length - 1)
530 builder.append(File.separator);
531 }
532 return builder.toString();
533 }
534
535
536
537
538
539
540
541
542 public static boolean isStaleFileHandle(IOException ioe) {
543 String msg = ioe.getMessage();
544 return msg != null
545 && msg.toLowerCase().matches("stale .*file .*handle");
546 }
547
548
549
550
551
552 static boolean isSymlink(File file) {
553 return Files.isSymbolicLink(file.toPath());
554 }
555
556
557
558
559
560
561
562 static long lastModified(File file) throws IOException {
563 return Files.getLastModifiedTime(file.toPath(), LinkOption.NOFOLLOW_LINKS)
564 .toMillis();
565 }
566
567
568
569
570
571
572 static void setLastModified(File file, long time) throws IOException {
573 Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(time));
574 }
575
576
577
578
579
580
581 static boolean exists(File file) {
582 return Files.exists(file.toPath(), LinkOption.NOFOLLOW_LINKS);
583 }
584
585
586
587
588
589
590 static boolean isHidden(File file) throws IOException {
591 return Files.isHidden(file.toPath());
592 }
593
594
595
596
597
598
599
600 public static void setHidden(File file, boolean hidden) throws IOException {
601 Files.setAttribute(file.toPath(), "dos:hidden", Boolean.valueOf(hidden),
602 LinkOption.NOFOLLOW_LINKS);
603 }
604
605
606
607
608
609
610
611 public static long getLength(File file) throws IOException {
612 Path nioPath = file.toPath();
613 if (Files.isSymbolicLink(nioPath))
614 return Files.readSymbolicLink(nioPath).toString()
615 .getBytes(Constants.CHARSET).length;
616 return Files.size(nioPath);
617 }
618
619
620
621
622
623
624 static boolean isDirectory(File file) {
625 return Files.isDirectory(file.toPath(), LinkOption.NOFOLLOW_LINKS);
626 }
627
628
629
630
631
632
633 static boolean isFile(File file) {
634 return Files.isRegularFile(file.toPath(), LinkOption.NOFOLLOW_LINKS);
635 }
636
637
638
639
640
641
642 public static boolean canExecute(File file) {
643 if (!isFile(file)) {
644 return false;
645 }
646 return Files.isExecutable(file.toPath());
647 }
648
649
650
651
652
653
654 static Attributes getFileAttributesBasic(FS fs, File file) {
655 try {
656 Path nioPath = file.toPath();
657 BasicFileAttributes readAttributes = nioPath
658 .getFileSystem()
659 .provider()
660 .getFileAttributeView(nioPath,
661 BasicFileAttributeView.class,
662 LinkOption.NOFOLLOW_LINKS).readAttributes();
663 Attributes attributes = new Attributes(fs, file,
664 true,
665 readAttributes.isDirectory(),
666 fs.supportsExecute() ? file.canExecute() : false,
667 readAttributes.isSymbolicLink(),
668 readAttributes.isRegularFile(),
669 readAttributes.creationTime().toMillis(),
670 readAttributes.lastModifiedTime().toMillis(),
671 readAttributes.isSymbolicLink() ? Constants
672 .encode(readSymLink(file)).length
673 : readAttributes.size());
674 return attributes;
675 } catch (IOException e) {
676 return new Attributes(file, fs);
677 }
678 }
679
680
681
682
683
684
685
686 public static Attributes getFileAttributesPosix(FS fs, File file) {
687 try {
688 Path nioPath = file.toPath();
689 PosixFileAttributes readAttributes = nioPath
690 .getFileSystem()
691 .provider()
692 .getFileAttributeView(nioPath,
693 PosixFileAttributeView.class,
694 LinkOption.NOFOLLOW_LINKS).readAttributes();
695 Attributes attributes = new Attributes(
696 fs,
697 file,
698 true,
699 readAttributes.isDirectory(),
700 readAttributes.permissions().contains(
701 PosixFilePermission.OWNER_EXECUTE),
702 readAttributes.isSymbolicLink(),
703 readAttributes.isRegularFile(),
704 readAttributes.creationTime().toMillis(),
705 readAttributes.lastModifiedTime().toMillis(),
706 readAttributes.size());
707 return attributes;
708 } catch (IOException e) {
709 return new Attributes(file, fs);
710 }
711 }
712
713
714
715
716
717
718 public static File normalize(File file) {
719 if (SystemReader.getInstance().isMacOS()) {
720
721
722 String normalized = Normalizer.normalize(file.getPath(),
723 Normalizer.Form.NFC);
724 return new File(normalized);
725 }
726 return file;
727 }
728
729
730
731
732
733
734 public static String normalize(String name) {
735 if (SystemReader.getInstance().isMacOS()) {
736 if (name == null)
737 return null;
738 return Normalizer.normalize(name, Normalizer.Form.NFC);
739 }
740 return name;
741 }
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756 public static File canonicalize(File file) {
757 if (file == null) {
758 return null;
759 }
760 try {
761 return file.getCanonicalFile();
762 } catch (IOException e) {
763 return file;
764 }
765 }
766
767 }