From 14cd43e6df8045c8f2879ddc8db0f59f45f2ce9c Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Thu, 23 Jan 2014 06:07:12 +0100 Subject: [PATCH] Use fileAttributes to get more attributes in one go On Windows the length reported by FileAttributes is the size of the target file (a bug, I guess) rather than the link, so we read the linke and look at the length of the link instead. Bug: 353771 Change-Id: I834b06d0447f84379612b8c9190fa77093617595 Signed-off-by: Matthias Sohn --- .../org/eclipse/jgit/util/FS_POSIX_Java7.java | 10 +- .../org/eclipse/jgit/util/FS_Win32_Java7.java | 8 + .../jgit/util/FS_Win32_Java7Cygwin.java | 8 + .../src/org/eclipse/jgit/util/FileUtil.java | 77 +++++++++ .../FileTreeIteratorWithTimeControl.java | 2 +- .../jgit/treewalk/FileTreeIterator.java | 65 +++----- .../src/org/eclipse/jgit/util/FS.java | 146 ++++++++++++++++++ 7 files changed, 267 insertions(+), 49 deletions(-) diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java index 6a9848179..e8307fc2b 100644 --- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java +++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java @@ -46,8 +46,6 @@ package org.eclipse.jgit.util; import java.io.File; import java.io.IOException; -import org.eclipse.jgit.util.FS; - /** * FS implementation for Java7 on unix like systems */ @@ -150,4 +148,12 @@ public class FS_POSIX_Java7 extends FS_POSIX { public void createSymLink(File path, String target) throws IOException { FileUtil.createSymLink(path, target); } + + /** + * @since 3.3 + */ + @Override + public Attributes getAttributes(File path) { + return FileUtil.getFileAttributesPosix(this, path); + } } diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java index 555163222..4a9d5f0bb 100644 --- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java +++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java @@ -151,4 +151,12 @@ public class FS_Win32_Java7 extends FS_Win32 { public void createSymLink(File path, String target) throws IOException { FileUtil.createSymLink(path, target); } + + /** + * @since 3.3 + */ + @Override + public Attributes getAttributes(File path) { + return FileUtil.getFileAttributesBasic(this, path); + } } diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java index 3db2e53e4..e40d7cf0b 100644 --- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java +++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java @@ -127,4 +127,12 @@ public class FS_Win32_Java7Cygwin extends FS_Win32_Cygwin { public void createSymLink(File path, String target) throws IOException { FileUtil.createSymLink(path, target); } + + /** + * @since 3.3 + */ + @Override + public Attributes getAttributes(File path) { + return FileUtil.getFileAttributesBasic(this, path); + } } diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java index 0fd19bcdd..78dc2c393 100644 --- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java +++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java @@ -47,15 +47,33 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; import java.text.Normalizer; import java.text.Normalizer.Form; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.util.FS.Attributes; class FileUtil { + static class Java7BasicAttributes extends Attributes { + + Java7BasicAttributes(FS fs, File fPath, boolean exists, + boolean isDirectory, boolean isExecutable, + boolean isSymbolicLink, boolean isRegularFile, + long creationTime, long lastModifiedTime, long length) { + super(fs, fPath, exists, isDirectory, isExecutable, isSymbolicLink, + isRegularFile, creationTime, lastModifiedTime, length); + } + } + static String readSymlink(File path) throws IOException { Path nioPath = path.toPath(); Path target = Files.readSymbolicLink(nioPath); @@ -145,4 +163,63 @@ class FileUtil { Files.delete(nioPath); } + static Attributes getFileAttributesBasic(FS fs, File path) { + try { + Path nioPath = path.toPath(); + BasicFileAttributes readAttributes = nioPath + .getFileSystem() + .provider() + .getFileAttributeView(nioPath, + BasicFileAttributeView.class, + LinkOption.NOFOLLOW_LINKS).readAttributes(); + Attributes attributes = new FileUtil.Java7BasicAttributes(fs, path, + true, + readAttributes.isDirectory(), + fs.supportsExecute() ? path.canExecute() : false, + readAttributes.isSymbolicLink(), + readAttributes.isRegularFile(), // + readAttributes.creationTime().toMillis(), // + readAttributes.lastModifiedTime().toMillis(), + readAttributes.isSymbolicLink() ? Constants + .encode(FileUtils.readSymLink(path)).length + : readAttributes.size()); + return attributes; + } catch (NoSuchFileException e) { + return new FileUtil.Java7BasicAttributes(fs, path, false, false, + false, false, false, 0L, 0L, 0L); + } catch (IOException e) { + return new Attributes(path, fs); + } + } + + static Attributes getFileAttributesPosix(FS fs, File path) { + try { + Path nioPath = path.toPath(); + PosixFileAttributes readAttributes = nioPath + .getFileSystem() + .provider() + .getFileAttributeView(nioPath, + PosixFileAttributeView.class, + LinkOption.NOFOLLOW_LINKS).readAttributes(); + Attributes attributes = new FileUtil.Java7BasicAttributes( + fs, + path, + true, // + readAttributes.isDirectory(), // + readAttributes.permissions().contains( + PosixFilePermission.OWNER_EXECUTE), + readAttributes.isSymbolicLink(), + readAttributes.isRegularFile(), // + readAttributes.creationTime().toMillis(), // + readAttributes.lastModifiedTime().toMillis(), + readAttributes.size()); + return attributes; + } catch (NoSuchFileException e) { + return new FileUtil.Java7BasicAttributes(fs, path, false, false, + false, false, false, 0L, 0L, 0L); + } catch (IOException e) { + return new Attributes(path, fs); + } + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java index 8ea1fd9e9..ff5730e72 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java @@ -95,7 +95,7 @@ public class FileTreeIteratorWithTimeControl extends FileTreeIterator { @Override public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader) { return new FileTreeIteratorWithTimeControl(this, - ((FileEntry) current()).file, fs, modTimes); + ((FileEntry) current()).getFile(), fs, modTimes); } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java index cb919ecbc..9e89791b8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -132,7 +132,7 @@ public class FileTreeIterator extends WorkingTreeIterator { @Override public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader) throws IncorrectObjectTypeException, IOException { - return new FileTreeIterator(this, ((FileEntry) current()).file, fs); + return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs); } private Entry[] entries() { @@ -149,13 +149,9 @@ public class FileTreeIterator extends WorkingTreeIterator { * Wrapper for a standard Java IO file */ static public class FileEntry extends Entry { - final File file; - private final FileMode mode; - private long length = -1; - - private long lastModified; + private FS.Attributes attributes; private FS fs; @@ -168,27 +164,17 @@ public class FileTreeIterator extends WorkingTreeIterator { * file system */ public FileEntry(final File f, FS fs) { - file = f; this.fs = fs; - - @SuppressWarnings("hiding") - FileMode mode = null; - try { - if (fs.isSymLink(f)) { - mode = FileMode.SYMLINK; - } else if (fs.isDirectory(f)) { - if (fs.exists(new File(f, Constants.DOT_GIT))) - mode = FileMode.GITLINK; - else - mode = FileMode.TREE; - } else if (fs.canExecute(file)) - mode = FileMode.EXECUTABLE_FILE; + attributes = fs.getAttributes(f); + if (attributes.isDirectory()) { + if (new File(f, Constants.DOT_GIT).exists()) + mode = FileMode.GITLINK; else - mode = FileMode.REGULAR_FILE; - } catch (IOException e) { - mode = FileMode.MISSING; - } - this.mode = mode; + mode = FileMode.TREE; + } else if (attributes.isExecutable()) + mode = FileMode.EXECUTABLE_FILE; + else + mode = FileMode.REGULAR_FILE; } @Override @@ -198,40 +184,27 @@ public class FileTreeIterator extends WorkingTreeIterator { @Override public String getName() { - return file.getName(); + return attributes.getName(); } @Override public long getLength() { - if (length < 0) { - try { - length = fs.length(file); - } catch (IOException e) { - length = 0; - } - } - return length; + return attributes.getLength(); } @Override public long getLastModified() { - if (lastModified == 0) { - try { - lastModified = fs.lastModified(file); - } catch (IOException e) { - lastModified = 0; - } - } - return lastModified; + return attributes.getLastModifiedTime(); } @Override public InputStream openInputStream() throws IOException { - if (fs.isSymLink(file)) - return new ByteArrayInputStream(fs.readSymLink(file).getBytes( + if (fs.isSymLink(getFile())) + return new ByteArrayInputStream(fs.readSymLink(getFile()) + .getBytes( Constants.CHARACTER_ENCODING)); else - return new FileInputStream(file); + return new FileInputStream(getFile()); } /** @@ -240,7 +213,7 @@ public class FileTreeIterator extends WorkingTreeIterator { * @return the underlying file of this entry */ public File getFile() { - return file; + return attributes.getFile(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index a5642f149..9e964fc36 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -634,4 +634,150 @@ public abstract class FS { this.value = value; } } + + /** + * File attributes we typically care for. + * + * @since 3.3 + */ + public static class Attributes { + + /** + * @return true if this are the attributes of a directory + */ + public boolean isDirectory() { + return isDirectory; + } + + /** + * @return true if this are the attributes of an executable file + */ + public boolean isExecutable() { + return isExecutable; + } + + /** + * @return true if this are the attributes of a symbolic link + */ + public boolean isSymbolicLink() { + return isSymbolicLink; + } + + /** + * @return true if this are the attributes of a regular file + */ + public boolean isRegularFile() { + return isRegularFile; + } + + /** + * @return the time when the file was created + */ + public long getCreationTime() { + return creationTime; + } + + /** + * @return the time (milliseconds since 1970-01-01) when this object was + * last modified + */ + public long getLastModifiedTime() { + return lastModifiedTime; + } + + private boolean isDirectory; + + private boolean isSymbolicLink; + + private boolean isRegularFile; + + private long creationTime; + + private long lastModifiedTime; + + private boolean isExecutable; + + private File file; + + private boolean exists; + + /** + * file length + */ + protected long length = -1; + + FS fs; + + Attributes(FS fs, File file, boolean exists, boolean isDirectory, + boolean isExecutable, boolean isSymbolicLink, + boolean isRegularFile, long creationTime, + long lastModifiedTime, long length) { + this.fs = fs; + this.file = file; + this.exists = exists; + this.isDirectory = isDirectory; + this.isExecutable = isExecutable; + this.isSymbolicLink = isSymbolicLink; + this.isRegularFile = isRegularFile; + this.creationTime = creationTime; + this.lastModifiedTime = lastModifiedTime; + this.length = length; + } + + /** + * Constructor when there are issues with reading + * + * @param fs + * @param path + */ + public Attributes(File path, FS fs) { + this.file = path; + this.fs = fs; + } + + /** + * @return length of this file object + */ + public long getLength() { + if (length == -1) + return length = file.length(); + return length; + } + + /** + * @return the filename + */ + public String getName() { + return file.getName(); + } + + /** + * @return the file the attributes apply to + */ + public File getFile() { + return file; + } + + boolean exists() { + return exists; + } + } + + /** + * @param path + * @return the file attributes we care for + * @since 3.3 + */ + public Attributes getAttributes(File path) { + boolean isDirectory = isDirectory(path); + boolean isFile = !isDirectory && path.isFile(); + assert path.exists() == isDirectory || isFile; + boolean exists = isDirectory || isFile; + boolean canExecute = exists && !isDirectory && canExecute(path); + boolean isSymlink = false; + long lastModified = exists ? path.lastModified() : 0L; + long createTime = 0L; + return new Attributes(this, path, exists, isDirectory, canExecute, + isSymlink, isFile, createTime, lastModified, -1); + } }