Getting the default root directory in Java - java

I'm making a basic file browser, and want to know how to get the default root directory. I know that java.io.File.listRoots() gives all the roots (for me it's A:\, C:\, D:\, E:\, F:\, G:\, H:\, I:\, L:\ T:\, U:\, X:\, Y:\, Z:\), but I want the one the user uses primarily (i.e. the one with the Operating system on it) so I know from where to start the browsing.

Not sure if this is of any help, but you could try:
import javax.swing.filechooser.*;
FileSystemView.getFileSystemView().getRoots()[0];
or
FileSystemView.getFileSystemView().getHomeDirectory();
or
System.getProperty("user.dir");
For the last snippet, you could get the root directory by navigating upward using getParent() until null is returned.

Getting the operating system root partition is only a thing on Windows since on Unix it's always /.
Hence, the following code works for Windows only:
System.getenv("SystemDrive");
It gets the SystemDrive environment variable value. This should always return the operating system's root partition e.g. C:.

Related

Eclipse RCP: InternalPlatform.getDefault().getUserLocation() doesen't return C:\Users\username\AppData\Roaming

I am currently migrating an Eclipse 3.0 application to 4.4. The user data was and still should be stored in the folder C:\Users\username\AppData\Roaming\applicationname
The application is using following code to read the directory:
public static String getUserDirectory()
{
String directory = InternalPlatform.getDefault().getUserLocation().getFile();
return directory;
}
I know the code is deprecated, but following code returns the same:
public static String getUserDirectory()
{
String directory = Platform.getUserLocation().getURL().getFile();
return directory;
}
They both return C:\Users\username\user but as I said the user data should be stored at C:\Users\username\AppData\Roaming\applicationname. Did the behaviour of those methods change?
How can I realize that I store my user data under C:\Users\username\AppData\Roaming\applicationname and my application can still find the directory?
I know this has to do something with environment-variables which I don't fully understand.
I don't have a 3.x target platform at hand to compare but C:\Users\username\user looks plain wrong.
If you are interested in the details, the constructor of EquinoxLocations computes the userLocation and adds the literal 'user' the the user's home directory if no default is specified.
Hence, if you start your application with -user #user.home or -Dosgi.user.area=#user.home, the user location will be set to C:\Users\username\. Still not what you are looking for, but at least a sane value.
I think this is a bug in Equinox and recommend to file a bugzilla. If it turns out that there is a good reason for this appraoch the bug entry will still serve as documentation/reasoning.
In the meanwhile you could obtain the home directory on Windows through System.getenv( "APPDATA" ). According to this post it will return the roaming home directory.
I solved the problem by adding three properties in the Configuration tab of my config.ini.product-file:
osgi.configuration.area =
#user.home/AppData/Roaming/ApplicationName/...
osgi.user.area =
#user.home/AppData/Roaming/ApplicationName/...
osgi.instance.area =
#user.home/AppData/Roaming/ApplicationName
Now my method as stated in my question reads the paths that are configured by those properties and the config.ini file which is generated looks exactly like the one of the old build with Eclipse 3.0.

Detecting whether an application was launched from a read-only file system on OS X

I want to know whether the user launched our Java-based application from a read-only file system like from a .dmg, so functions like auto-update will be able to show meaningful information instead of aborting with an error. I first thought checking the .app's path would be sufficient (when launched from a .dmg it is something like /Volumes/MyApp 1.2.3/MyApp.app, but this won't work, because the user might have installed the application on a different partition. What other things may I check?
You can use -[NSURL getResourceValue:forKey:error:] with the key NSURLVolumeIsReadOnlyKey. You would apply this to the app bundle URL as returned by [[NSBundle mainBundle] bundleURL]. So:
NSBundle* bundle = [NSBundle mainBundle];
NSURL* bundleURL = bundle.bundleURL;
NSNumber* readOnly;
NSError* error;
if ([bundleURL getResourceValue:&readOnly forKey:NSURLVolumeIsReadOnlyKey error:&error])
{
BOOL isReadOnly = [readOnly boolValue];
// act on isReadOnly value
}
else
{
// Handle error
}
If OSX is POSIX compliant, to determine if filesystem is mounted R/O, You can use statvfs() or fstatvfs(), returned struct statvfs field f_flag should have ST_RDONLY bit set for R/O filesystem.
As it was pointed in comments, check if this information is correctly provided by OS.
JNA and this question may be usefull for Java.
A few more ideas, which may be usefull here (access(), open(), utime() ).
OS X specific statfs() may be used too, but this function is not portable (Linux and *BSD have slightly different statfs() functions).
You can also check directly from Java whether a certain path points to something within a read-only directory by querying the FileStore associated with your path:
File classpathRoot = new File(MyClass.class.getClassLoader().getResource("").getPath());
/* getPath() actually returns a String instead of a Path object,
* so we need to take this little detour */
Path yourAppPath = classpathRoot.toPath();
boolean isReadOnly = Files.getFileStore(yourAppPath).isReadOnly();

Setting the last-modified-time of a directory opened for ReadDirectoryChangesW

I hava a Java program that needs to monitor a directory tree for changes. I have JNI code that uses ReadDirectoryChangesW(). The directory is opened like:
HANDLE dirHandle = CreateFile(
path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL
);
and then I pass dirHandle to ReadDirectoryChangesW(). All of that works just fine.
The problem is that other parts of the code (on the Java side) use File.setLastModified() to "touch" files or directories (update their timestamps to be "now"). This generally works; however, it fails when it tried to "touch" the directory that was opened using CreateFile().
To see what Windows error is actually occurring, I looked at the JDK source for File.setLastModified() and reimplemented it in my own code with the addition of printing the error from GetLastError(); the error is:
ERROR_SHARING_VIOLATION (error 32)
"The process cannot access the file because it is being used by another process."
WTF? It's the same process. I even passed FILE_SHARE_READ and FILE_SHARE_WRITE to CreateFile().
Is there a way to make this work?
More Info
The native code implementation of File.setLastModified() in the JDK does a:
h = CreateFileW(pathbuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
If I change the first 0 to FILE_SHARE_READ | FILE_SHARE_WRITE, it all works. So it seems that the JDK implementation is a little broken. :(
So my question now becomes: Is there a way to make this work without having to use my own (re)implementation of File.setLastModified()?
Although the error message is a bit misleading in this case, what you're seeing is normal behaviour.
By opening the directory with dwShareMode set to zero, the JDK is, in effect, asking for exclusive access, which will cause any other access attempt to fail with a sharing violation error. This applies equally to accesses
from other processes and from within your own process.
The CreateFile documentation describes the dwShareMode parameter:
If this parameter is zero and CreateFile succeeds, the file or device cannot be shared and cannot be opened again until the handle to the file or device is closed.
You cannot request a sharing mode that conflicts with the access mode that is specified in an existing request that has an open handle. CreateFile would fail and the GetLastError function would return ERROR_SHARING_VIOLATION.
So, it seems you've answered your own question: you need a custom setLastModified function that specifies FILE_SHARE_READ | FILE_SHARE_WRITE when accessing the directory.

Java reports alias (symlink) as size 0 on Mac OSX. How do I get the true file size?

File file = new File("path to file alias foo");
where "path to file alias foo" is an alias reports file size to be 0 instead of the actual file size. I found a workaround to test for aliases:
public boolean isLink() {
try {
if (file.getAbsolutePath().equals(file.getCanonicalPath())) {
return false;
}
} catch (IOException ex) {
logger.severe(ex.getMessage());
}
return true;
}
EDIT Actually this code does not work, as pointed out by a poster below. I was trying to adapt a solution from a linux symlink example, but I didn't realize that finder aliases and symlinks were not the same.
NOT! this seems to work, but ....
file.getCanonicalFile().length();
still reports file length to be 0. Can anyone point me in the right direction?
Finder aliases are a different beast altogether from normal symbolic links. The *nix tools on OS X are not aware of aliases at all, because they're stored in the resource fork, I believe. If you install osxutils, you can use this shell command to get the target of an alias:
hfsdata -e the-alias
From Java, I don't know of a better way of doing this other than calling Runtime.exec(...).
Also, I just a did a quick check, and your function for detecting aliases does not work. AFAICT, Java is not aware of Finder aliases. If you really want to support them, then you'll either need to use something like osxutils, or use some platform-specific code to read resource forks (will probably involve JNI). Neither option is pretty.
If you go the JNI route, check out the Alias Manager Reference documentation. The relevant functions are FSIsAliasFile and FSResolveAliasFile.
You can use the FileRef Interface from the O'Reilly Java NIO API. I believe the getAttribute() method can handle symbolic links as you want, but I have not tried it on Mac OSX. From the docs:
The options array may be used to
indicate how symbolic links are
handled for the case that the file is
a symbolic link. By default, symbolic
links are followed and the file
attribute of the final target of the
link is read. If the option
NOFOLLOW_LINKS is present then
symbolic links are not followed and so
the method returns the file attribute
of the symbolic link.
size = new File(file.getCanonicalPath()).length();

In Java what exactly does File.canExecute() do?

I have created a plain file which does not have execute permission but when I create a Java File object using this file's path/name and then call File.canExecute() I get true as the result, whereas I would expect this method call to return false. Can someone explain what I'm missing here?
Solaris:
$ touch /tmp/nonexecutable
$ ls -l /tmp/nonexecutable
-rw-r--r-- 1 root root 0 May 21 07:48 /tmp/nonexecutable
Java:
String pathName = "/tmp/nonexecutable";
File myFile = new File(pathName);
if (!myFile.canExecute())
{
String errorMessage = "The file is not executable.";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}
Thanks in advance for your help.
--James
Nothing to do with Java - you're running as root, and root is allowed everything, not matter what the permissions say.
Though I'm not an expert, and this will not answer your question properly, I'd like to add that this behavior is not specific to Java. From the find (GNU findutils) 4.4.0 manpage on my Ubuntu 8.10 install, regarding the -executable flag:
Matches files which are
executable and directories which are
searchable (in a file name resolution
sense). This takes into account
access control lists and other
permissions artefacts which the -perm
test ignores. This test makes
use of the access(2) system call,
and so can be fooled by NFS servers
which do UID mapping (or
root-squashing), since many systems
implement access(2) in the client’s
kernel and so cannot make use of the
UID mapping information held on the
server. Because this test is
based only on the result of the
access(2) system call, there is no
guarantee that a file for which this
test succeeds can actually be
executed.
Here is a bug which was opened on JDK on this:
http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=8b833c54cb93d6c9cf416667dc02?bug_id=6379654
The conclusion is that File.canExecute() simply translates into a native posix call to access(path, X_OK). Linux returns false and solaris returns true for that call when run as root.
Finally, the bug was closed as Wont Fix! :)

Categories