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 public static void createSymLink(File path, String target)
406 throws IOException {
407 Path nioPath = path.toPath();
408 if (Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS)) {
409 Files.delete(nioPath);
410 }
411 if (SystemReader.getInstance().isWindows()) {
412 target = target.replace('/', '\\');
413 }
414 Path nioTarget = new File(target).toPath();
415 Files.createSymbolicLink(nioPath, nioTarget);
416 }
417
418
419
420
421
422
423
424 public static String readSymLink(File path) throws IOException {
425 Path nioPath = path.toPath();
426 Path target = Files.readSymbolicLink(nioPath);
427 String targetString = target.toString();
428 if (SystemReader.getInstance().isWindows()) {
429 targetString = targetString.replace('\\', '/');
430 } else if (SystemReader.getInstance().isMacOS()) {
431 targetString = Normalizer.normalize(targetString, Form.NFC);
432 }
433 return targetString;
434 }
435
436
437
438
439
440
441
442
443
444
445
446
447 public static File createTempDir(String prefix, String suffix, File dir)
448 throws IOException {
449 final int RETRIES = 1;
450 for (int i = 0; i < RETRIES; i++) {
451 File tmp = File.createTempFile(prefix, suffix, dir);
452 if (!tmp.delete())
453 continue;
454 if (!tmp.mkdir())
455 continue;
456 return tmp;
457 }
458 throw new IOException(JGitText.get().cannotCreateTempDir);
459 }
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491 public static String relativize(String base, String other) {
492 if (base.equals(other))
493 return "";
494
495 final boolean ignoreCase = !FS.DETECTED.isCaseSensitive();
496 final String[] baseSegments = base.split(Pattern.quote(File.separator));
497 final String[] otherSegments = other.split(Pattern
498 .quote(File.separator));
499
500 int commonPrefix = 0;
501 while (commonPrefix < baseSegments.length
502 && commonPrefix < otherSegments.length) {
503 if (ignoreCase
504 && baseSegments[commonPrefix]
505 .equalsIgnoreCase(otherSegments[commonPrefix]))
506 commonPrefix++;
507 else if (!ignoreCase
508 && baseSegments[commonPrefix]
509 .equals(otherSegments[commonPrefix]))
510 commonPrefix++;
511 else
512 break;
513 }
514
515 final StringBuilder builder = new StringBuilder();
516 for (int i = commonPrefix; i < baseSegments.length; i++)
517 builder.append("..").append(File.separator);
518 for (int i = commonPrefix; i < otherSegments.length; i++) {
519 builder.append(otherSegments[i]);
520 if (i < otherSegments.length - 1)
521 builder.append(File.separator);
522 }
523 return builder.toString();
524 }
525
526
527
528
529
530
531
532
533 public static boolean isStaleFileHandle(IOException ioe) {
534 String msg = ioe.getMessage();
535 return msg != null
536 && msg.toLowerCase().matches("stale .*file .*handle");
537 }
538
539
540
541
542
543 static boolean isSymlink(File file) {
544 return Files.isSymbolicLink(file.toPath());
545 }
546
547
548
549
550
551
552
553 static long lastModified(File file) throws IOException {
554 return Files.getLastModifiedTime(file.toPath(), LinkOption.NOFOLLOW_LINKS)
555 .toMillis();
556 }
557
558
559
560
561
562
563 static void setLastModified(File file, long time) throws IOException {
564 Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(time));
565 }
566
567
568
569
570
571
572 static boolean exists(File file) {
573 return Files.exists(file.toPath(), LinkOption.NOFOLLOW_LINKS);
574 }
575
576
577
578
579
580
581 static boolean isHidden(File file) throws IOException {
582 return Files.isHidden(file.toPath());
583 }
584
585
586
587
588
589
590
591 public static void setHidden(File file, boolean hidden) throws IOException {
592 Files.setAttribute(file.toPath(), "dos:hidden", Boolean.valueOf(hidden),
593 LinkOption.NOFOLLOW_LINKS);
594 }
595
596
597
598
599
600
601
602 public static long getLength(File file) throws IOException {
603 Path nioPath = file.toPath();
604 if (Files.isSymbolicLink(nioPath))
605 return Files.readSymbolicLink(nioPath).toString()
606 .getBytes(Constants.CHARSET).length;
607 return Files.size(nioPath);
608 }
609
610
611
612
613
614
615 static boolean isDirectory(File file) {
616 return Files.isDirectory(file.toPath(), LinkOption.NOFOLLOW_LINKS);
617 }
618
619
620
621
622
623
624 static boolean isFile(File file) {
625 return Files.isRegularFile(file.toPath(), LinkOption.NOFOLLOW_LINKS);
626 }
627
628
629
630
631
632
633 public static boolean canExecute(File file) {
634 if (!isFile(file)) {
635 return false;
636 }
637 return Files.isExecutable(file.toPath());
638 }
639
640
641
642
643
644
645 static Attributes getFileAttributesBasic(FS fs, File file) {
646 try {
647 Path nioPath = file.toPath();
648 BasicFileAttributes readAttributes = nioPath
649 .getFileSystem()
650 .provider()
651 .getFileAttributeView(nioPath,
652 BasicFileAttributeView.class,
653 LinkOption.NOFOLLOW_LINKS).readAttributes();
654 Attributes attributes = new Attributes(fs, file,
655 true,
656 readAttributes.isDirectory(),
657 fs.supportsExecute() ? file.canExecute() : false,
658 readAttributes.isSymbolicLink(),
659 readAttributes.isRegularFile(),
660 readAttributes.creationTime().toMillis(),
661 readAttributes.lastModifiedTime().toMillis(),
662 readAttributes.isSymbolicLink() ? Constants
663 .encode(readSymLink(file)).length
664 : readAttributes.size());
665 return attributes;
666 } catch (IOException e) {
667 return new Attributes(file, fs);
668 }
669 }
670
671
672
673
674
675
676
677 public static Attributes getFileAttributesPosix(FS fs, File file) {
678 try {
679 Path nioPath = file.toPath();
680 PosixFileAttributes readAttributes = nioPath
681 .getFileSystem()
682 .provider()
683 .getFileAttributeView(nioPath,
684 PosixFileAttributeView.class,
685 LinkOption.NOFOLLOW_LINKS).readAttributes();
686 Attributes attributes = new Attributes(
687 fs,
688 file,
689 true,
690 readAttributes.isDirectory(),
691 readAttributes.permissions().contains(
692 PosixFilePermission.OWNER_EXECUTE),
693 readAttributes.isSymbolicLink(),
694 readAttributes.isRegularFile(),
695 readAttributes.creationTime().toMillis(),
696 readAttributes.lastModifiedTime().toMillis(),
697 readAttributes.size());
698 return attributes;
699 } catch (IOException e) {
700 return new Attributes(file, fs);
701 }
702 }
703
704
705
706
707
708
709 public static File normalize(File file) {
710 if (SystemReader.getInstance().isMacOS()) {
711
712
713 String normalized = Normalizer.normalize(file.getPath(),
714 Normalizer.Form.NFC);
715 return new File(normalized);
716 }
717 return file;
718 }
719
720
721
722
723
724
725 public static String normalize(String name) {
726 if (SystemReader.getInstance().isMacOS()) {
727 if (name == null)
728 return null;
729 return Normalizer.normalize(name, Normalizer.Form.NFC);
730 }
731 return name;
732 }
733 }