summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NativeCode.mk4
-rw-r--r--luni/src/main/java/java/io/File.java208
-rw-r--r--luni/src/main/native/java_io_File.cpp60
-rw-r--r--luni/src/main/native/readlink.cpp42
-rw-r--r--luni/src/main/native/readlink.h25
-rw-r--r--luni/src/main/native/realpath.cpp125
-rw-r--r--luni/src/main/native/sub.mk2
-rw-r--r--luni/src/test/java/libcore/java/io/FileTest.java14
8 files changed, 272 insertions, 208 deletions
diff --git a/NativeCode.mk b/NativeCode.mk
index 4c6ffa5..57c2457 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -94,8 +94,8 @@ endif
# Define the rules.
LOCAL_SRC_FILES := $(core_src_files)
-LOCAL_C_INCLUDES := $(core_c_includes)
-LOCAL_SHARED_LIBRARIES := $(core_shared_libraries)
+LOCAL_C_INCLUDES := $(core_c_includes) bionic/ bionic/libstdc++/include external/stlport/stlport
+LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libstlport
LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libjavacore
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index ffc20aa..1e1d625 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -105,7 +105,7 @@ public class File implements Serializable, Comparable<File> {
/**
* The path we return from getAbsolutePath, and pass down to native code.
*/
- private String absolutePath;
+ private transient String absolutePath;
static {
// The default protection domain grants access to these properties.
@@ -262,8 +262,8 @@ public class File implements Serializable, Comparable<File> {
/**
* Lists the file system roots. The Java platform may support zero or more
* file systems, each with its own platform-dependent root. Further, the
- * canonical pathname of any file on the system will always begin with one
- * of the returned file system roots.
+ * {@link #getCanonicalPath canonical} path of any file on the system will
+ * always begin with one of the returned file system roots.
*
* @return the array of file system roots.
*/
@@ -436,9 +436,12 @@ public class File implements Serializable, Comparable<File> {
private static native boolean existsImpl(String path);
/**
- * Returns the absolute path of this file.
+ * Returns the absolute path of this file. An absolute path is a path that starts at a root
+ * of the file system. On Android, there is only one root: {@code /}.
*
- * @return the absolute file path.
+ * <p>A common use for absolute paths is when passing paths to a {@code Process} as
+ * command-line arguments, to remove the requirement implied by relative paths, that the
+ * child must have the same working directory as its parent.
*/
public String getAbsolutePath() {
return absolutePath;
@@ -446,191 +449,46 @@ public class File implements Serializable, Comparable<File> {
/**
* Returns a new file constructed using the absolute path of this file.
- *
- * @return a new file from this file's absolute path.
- * @see java.lang.SecurityManager#checkPropertyAccess
+ * Equivalent to {@code new File(this.getAbsolutePath())}.
*/
public File getAbsoluteFile() {
return new File(this.getAbsolutePath());
}
/**
- * Returns the absolute path of this file with all references resolved. An
- * <em>absolute</em> path is one that begins at the root of the file
- * system. The canonical path is one in which all references have been
- * resolved. For the cases of '..' and '.', where the file system supports
- * parent and working directory respectively, these are removed and replaced
- * with a direct directory reference. If the file does not exist,
- * getCanonicalPath() may not resolve any references and simply returns an
- * absolute path name or throws an IOException.
+ * Returns the canonical path of this file.
+ * An <i>absolute</i> path is one that begins at the root of the file system.
+ * A <i>canonical</i> path is an absolute path with symbolic links
+ * and references to "." or ".." resolved. If a path element does not exist (or
+ * is not searchable), there is a conflict between interpreting canonicalization
+ * as a textual operation (where "a/../b" is "b" even if "a" does not exist) .
+ *
+ * <p>Most callers should use {@link #getAbsolutePath} instead. A canonical path is
+ * significantly more expensive to compute, and not generally useful. The primary
+ * use for canonical paths is determining whether two paths point to the same file by
+ * comparing the canonicalized paths.
+ *
+ * <p>It can be actively harmful to use a canonical path, specifically because
+ * canonicalization removes symbolic links. It's wise to assume that a symbolic link
+ * is present for a reason, and that that reason is because the link may need to change.
+ * Canonicalization removes this layer of indirection. Good code should generally avoid
+ * caching canonical paths.
*
* @return the canonical path of this file.
* @throws IOException
* if an I/O error occurs.
*/
public String getCanonicalPath() throws IOException {
- // BEGIN android-removed
- // Caching the canonical path is bogus. Users facing specific
- // performance problems can perform their own caching, with
- // eviction strategies that are appropriate for their application.
- // A VM-wide cache with no mechanism to evict stale elements is a
- // disservice to applications that need up-to-date data.
- // String canonPath = FileCanonPathCache.get(absPath);
- // if (canonPath != null) {
- // return canonPath;
- // }
- // END android-removed
-
- // TODO: rewrite getCanonicalPath, resolve, and resolveLink.
-
- String result = absolutePath;
- if (separatorChar == '/') {
- // resolve the full path first
- result = resolveLink(result, result.length(), false);
- // resolve the parent directories
- result = resolve(result);
- }
- int numSeparators = 1;
- for (int i = 0; i < result.length(); ++i) {
- if (result.charAt(i) == separatorChar) {
- numSeparators++;
- }
- }
- int[] sepLocations = new int[numSeparators];
- int rootLoc = 0;
- if (separatorChar != '/') {
- if (result.charAt(0) == '\\') {
- rootLoc = (result.length() > 1 && result.charAt(1) == '\\') ? 1 : 0;
- } else {
- rootLoc = 2; // skip drive i.e. c:
- }
- }
-
- char[] newResult = new char[result.length() + 1];
- int newLength = 0, lastSlash = 0, foundDots = 0;
- sepLocations[lastSlash] = rootLoc;
- for (int i = 0; i <= result.length(); ++i) {
- if (i < rootLoc) {
- newResult[newLength++] = result.charAt(i);
- } else {
- if (i == result.length() || result.charAt(i) == separatorChar) {
- if (i == result.length() && foundDots == 0) {
- break;
- }
- if (foundDots == 1) {
- /* Don't write anything, just reset and continue */
- foundDots = 0;
- continue;
- }
- if (foundDots > 1) {
- /* Go back N levels */
- lastSlash = lastSlash > (foundDots - 1) ? lastSlash - (foundDots - 1) : 0;
- newLength = sepLocations[lastSlash] + 1;
- foundDots = 0;
- continue;
- }
- sepLocations[++lastSlash] = newLength;
- newResult[newLength++] = separatorChar;
- continue;
- }
- if (result.charAt(i) == '.') {
- foundDots++;
- continue;
- }
- /* Found some dots within text, write them out */
- if (foundDots > 0) {
- for (int j = 0; j < foundDots; j++) {
- newResult[newLength++] = '.';
- }
- }
- newResult[newLength++] = result.charAt(i);
- foundDots = 0;
- }
- }
- // remove trailing slash
- if (newLength > (rootLoc + 1) && newResult[newLength - 1] == separatorChar) {
- newLength--;
- }
- return new String(newResult, 0, newLength);
- }
-
- /*
- * Resolve symbolic links in the parent directories.
- */
- private static String resolve(String path) throws IOException {
- int last = 1;
- String linkPath = path;
- String bytes;
- boolean done;
- for (int i = 1; i <= path.length(); i++) {
- if (i == path.length() || path.charAt(i) == separatorChar) {
- done = i >= path.length() - 1;
- // if there is only one segment, do nothing
- if (done && linkPath.length() == 1) {
- return path;
- }
- boolean inPlace = false;
- if (linkPath.equals(path)) {
- bytes = path;
- // if there are no symbolic links, truncate the path instead of copying
- if (!done) {
- inPlace = true;
- path = path.substring(0, i);
- }
- } else {
- int nextSize = i - last + 1;
- int linkSize = linkPath.length();
- if (linkPath.charAt(linkSize - 1) == separatorChar) {
- linkSize--;
- }
- bytes = linkPath.substring(0, linkSize) +
- path.substring(last - 1, last - 1 + nextSize);
- // the full path has already been resolved
- }
- if (done) {
- return bytes;
- }
- linkPath = resolveLink(bytes, inPlace ? i : bytes.length(), true);
- if (inPlace) {
- // path[i] = '/';
- path = path.substring(0, i) + '/' + (i + 1 < path.length() ? path.substring(i + 1) : "");
- }
- last = i + 1;
- }
- }
- throw new InternalError();
+ return realpath(absolutePath);
}
- /*
- * Resolve a symbolic link. While the path resolves to an existing path,
- * keep resolving. If an absolute link is found, resolve the parent
- * directories if resolveAbsolute is true.
+ /**
+ * TODO: move this stuff to libcore.os.
+ * @hide
*/
- private static String resolveLink(String path, int length, boolean resolveAbsolute) throws IOException {
- boolean restart = false;
- do {
- String fragment = path.substring(0, length);
- String target = readlink(fragment);
- if (target.equals(fragment)) {
- break;
- }
- if (target.charAt(0) == separatorChar) {
- // The link target was an absolute path, so we may need to start again.
- restart = resolveAbsolute;
- path = target + path.substring(length);
- } else {
- path = path.substring(0, path.lastIndexOf(separatorChar, length - 1) + 1) + target;
- }
- length = path.length();
- } while (existsImpl(path));
- // resolve the parent directories
- if (restart) {
- return resolve(path);
- }
- return path;
- }
-
- private static native String readlink(String filePath);
+ public static native void symlink(String oldPath, String newPath);
+ private static native String realpath(String path);
+ private static native String readlink(String path);
/**
* Returns a new file created using the canonical path of this file.
diff --git a/luni/src/main/native/java_io_File.cpp b/luni/src/main/native/java_io_File.cpp
index ea8d12b..2419e2e 100644
--- a/luni/src/main/native/java_io_File.cpp
+++ b/luni/src/main/native/java_io_File.cpp
@@ -25,6 +25,9 @@
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
#include "StaticAssert.h"
+#include "readlink.h"
+
+#include <string>
#include <dirent.h>
#include <errno.h>
@@ -32,8 +35,8 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/vfs.h>
#include <sys/types.h>
+#include <sys/vfs.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>
@@ -126,24 +129,27 @@ static jstring File_readlink(JNIEnv* env, jclass, jstring javaPath) {
return NULL;
}
- // We can't know how big a buffer readlink(2) will need, so we need to
- // loop until it says "that fit".
- size_t bufSize = 512;
- while (true) {
- LocalArray<512> buf(bufSize);
- ssize_t len = readlink(path.c_str(), &buf[0], buf.size() - 1);
- if (len == -1) {
- // An error occurred.
- return javaPath;
- }
- if (static_cast<size_t>(len) < buf.size() - 1) {
- // The buffer was big enough.
- buf[len] = '\0'; // readlink(2) doesn't NUL-terminate.
- return env->NewStringUTF(&buf[0]);
- }
- // Try again with a bigger buffer.
- bufSize *= 2;
+ std::string result;
+ if (!readlink(path.c_str(), result)) {
+ jniThrowIOException(env, errno);
+ return NULL;
+ }
+ return env->NewStringUTF(result.c_str());
+}
+
+static jstring File_realpath(JNIEnv* env, jclass, jstring javaPath) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return NULL;
+ }
+
+ extern bool realpath(const char* path, std::string& resolved);
+ std::string result;
+ if (!realpath(path.c_str(), result)) {
+ jniThrowIOException(env, errno);
+ return NULL;
}
+ return env->NewStringUTF(result.c_str());
}
static jboolean File_setLastModifiedImpl(JNIEnv* env, jclass, jstring javaPath, jlong ms) {
@@ -421,6 +427,22 @@ static jboolean File_renameToImpl(JNIEnv* env, jclass, jstring javaOldPath, jstr
return (rename(oldPath.c_str(), newPath.c_str()) == 0);
}
+static void File_symlink(JNIEnv* env, jclass, jstring javaOldPath, jstring javaNewPath) {
+ ScopedUtfChars oldPath(env, javaOldPath);
+ if (oldPath.c_str() == NULL) {
+ return;
+ }
+
+ ScopedUtfChars newPath(env, javaNewPath);
+ if (newPath.c_str() == NULL) {
+ return;
+ }
+
+ if (symlink(oldPath.c_str(), newPath.c_str()) == -1) {
+ jniThrowIOException(env, errno);
+ }
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(File, canExecuteImpl, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(File, canReadImpl, "(Ljava/lang/String;)Z"),
@@ -438,11 +460,13 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(File, listImpl, "(Ljava/lang/String;)[Ljava/lang/String;"),
NATIVE_METHOD(File, mkdirImpl, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(File, readlink, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(File, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(File, renameToImpl, "(Ljava/lang/String;Ljava/lang/String;)Z"),
NATIVE_METHOD(File, setExecutableImpl, "(Ljava/lang/String;ZZ)Z"),
NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"),
NATIVE_METHOD(File, setReadableImpl, "(Ljava/lang/String;ZZ)Z"),
NATIVE_METHOD(File, setWritableImpl, "(Ljava/lang/String;ZZ)Z"),
+ NATIVE_METHOD(File, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"),
};
int register_java_io_File(JNIEnv* env) {
return jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/readlink.cpp b/luni/src/main/native/readlink.cpp
new file mode 100644
index 0000000..555d515
--- /dev/null
+++ b/luni/src/main/native/readlink.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LocalArray.h"
+#include "readlink.h"
+
+#include <string>
+#include <unistd.h>
+
+bool readlink(const char* path, std::string& result) {
+ // We can't know how big a buffer readlink(2) will need, so we need to
+ // loop until it says "that fit".
+ size_t bufSize = 512;
+ while (true) {
+ LocalArray<512> buf(bufSize);
+ ssize_t len = readlink(path, &buf[0], buf.size());
+ if (len == -1) {
+ // An error occurred.
+ return false;
+ }
+ if (static_cast<size_t>(len) < buf.size()) {
+ // The buffer was big enough.
+ result.assign(&buf[0], len);
+ return true;
+ }
+ // Try again with a bigger buffer.
+ bufSize *= 2;
+ }
+}
diff --git a/luni/src/main/native/readlink.h b/luni/src/main/native/readlink.h
new file mode 100644
index 0000000..14031dc
--- /dev/null
+++ b/luni/src/main/native/readlink.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+/**
+ * Fills 'result' with the contents of the symbolic link 'path'. Sets errno and returns false on
+ * failure, returns true on success. The contents of 'result' on failure are undefined. Possible
+ * errors are those defined for readlink(2), except that this function takes care of sizing the
+ * buffer appropriately.
+ */
+bool readlink(const char* path, std::string& result);
diff --git a/luni/src/main/native/realpath.cpp b/luni/src/main/native/realpath.cpp
new file mode 100644
index 0000000..d1960a4
--- /dev/null
+++ b/luni/src/main/native/realpath.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "readlink.h"
+
+#include <string>
+
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/**
+ * This differs from realpath(3) mainly in its behavior when a path element does not exist or can
+ * not be searched. realpath(3) treats that as an error and gives up, but we have Java-compatible
+ * behavior where we just assume the path element was not a symbolic link. This leads to a textual
+ * treatment of ".." from that point in the path, which may actually lead us back to a path we
+ * can resolve (as in "/tmp/does-not-exist/../blah.txt" which would be an error for realpath(3)
+ * but "/tmp/blah.txt" under the traditional Java interpretation).
+ *
+ * This implementation also removes all the fixed-length buffers of the C original.
+ */
+bool realpath(const char* path, std::string& resolved) {
+ // 'path' must be an absolute path.
+ if (path[0] != '/') {
+ errno = EINVAL;
+ return false;
+ }
+
+ resolved = "/";
+ if (path[1] == '\0') {
+ return true;
+ }
+
+ // Iterate over path components in 'left'.
+ int symlinkCount = 0;
+ std::string left(path + 1);
+ while (!left.empty()) {
+ // Extract the next path component.
+ size_t nextSlash = left.find('/');
+ std::string nextPathComponent = left.substr(0, nextSlash);
+ if (nextSlash != std::string::npos) {
+ left.erase(0, nextSlash + 1);
+ } else {
+ left.clear();
+ }
+ if (nextPathComponent.empty()) {
+ continue;
+ } else if (nextPathComponent == ".") {
+ continue;
+ } else if (nextPathComponent == "..") {
+ // Strip the last path component except when we have single "/".
+ if (resolved.size() > 1) {
+ resolved.erase(resolved.rfind('/'));
+ }
+ continue;
+ }
+
+ // Append the next path component.
+ if (resolved[resolved.size() - 1] != '/') {
+ resolved += '/';
+ }
+ resolved += nextPathComponent;
+
+ // See if we've got a symbolic link, and resolve it if so.
+ struct stat sb;
+ if (lstat(resolved.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode)) {
+ if (symlinkCount++ > MAXSYMLINKS) {
+ errno = ELOOP;
+ return false;
+ }
+
+ std::string symlink;
+ if (!readlink(resolved.c_str(), symlink)) {
+ return false;
+ }
+ if (symlink[0] == '/') {
+ // The symbolic link is absolute, so we need to start from scratch.
+ resolved = "/";
+ } else if (resolved.size() > 1) {
+ // The symbolic link is relative, so we just lose the last path component (which
+ // was the link).
+ resolved.erase(resolved.rfind('/'));
+ }
+
+ if (!left.empty()) {
+ const char* maybeSlash = (symlink[symlink.size() - 1] != '/') ? "/" : "";
+ left = symlink + maybeSlash + left;
+ } else {
+ left = symlink;
+ }
+ }
+ }
+
+ // Remove trailing slash except when the resolved pathname is a single "/".
+ if (resolved.size() > 1 && resolved[resolved.size() - 1] == '/') {
+ resolved.erase(resolved.size() - 1, 1);
+ }
+ return true;
+}
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 774e8cb..cb6a68c 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -52,6 +52,8 @@ LOCAL_SRC_FILES := \
org_apache_harmony_luni_platform_OSNetworkSystem.cpp \
org_apache_harmony_luni_util_FloatingPointParser.cpp \
org_apache_harmony_xml_ExpatParser.cpp \
+ readlink.cpp \
+ realpath.cpp \
valueOf.cpp
LOCAL_C_INCLUDES += \
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index cd9b877..535975d 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -186,18 +186,6 @@ public class FileTest extends junit.framework.TestCase {
}
private static void ln_s(String target, String linkName) throws Exception {
- String[] args = new String[] { "ln", "-s", target, linkName };
- // System.err.println("ln -s " + target + " " + linkName);
- Process p = Runtime.getRuntime().exec(args);
- int result = p.waitFor();
- if (result != 0) {
- BufferedReader r = new BufferedReader(new InputStreamReader(p.getErrorStream()));
- String line;
- while ((line = r.readLine()) != null) {
- System.err.println(line);
- }
- fail("ln -s " + target + " " + linkName + " failed. " +
- "Does that file system support symlinks?");
- }
+ File.symlink(target, linkName);
}
}