CreateProcessW fails (ACESS_DENIED) - java

I currently convert an application to use CreateProcessW() instead of Runtime.exec() as I need the information it provides. However any call to CreateProcessW() fails with the error code 5 (ACCESS DENIED). I have been unable to find out why this happens as Runtime.exec() runs fine in the same case.
My error could be in one of the following code snippets, the method call and the jna interface.
public ProcessInfo createProcess(String dir, String name){
ProcessInfo pi = new ProcessInfo();
StartupInfo start = new StartupInfo();
mem.CreateProcessW(new WString(name),
null,
null,
null,
false,
0,
null,
new WString(dir),
start.getPointer(),
pi.getPointer());
return pi;
}
My definition of CreateProcessW
boolean CreateProcessW(WString apname,
char[] comline,
Pointer p,
Pointer p2,
boolean inheritHandles,
int createFlags,
String environment,
WString directory,
Pointer startinf,
Pointer processInfo);
Additional Info:
Runtime.exec() succeeds with the given Strings
The size of StartupInfo is set
Testenvironment used: WinXP SP3 and Netbeans 6.9.1
Example parameters used:
Name: moviemk.exe
Dir: C:\Programme\Movie Maker\
Also tested with different paths, so not a whitespace problem
Thanks
Update:
As it turns out the error was caused by my calling code switching around working dir and exe path after I checked them. Because of the resulting access denied I actually thought that it at least found the exe. I will add an IllegalArgumentException to take care of that problem.
Since I had the additional error with the exe being relative to the working dir I will accept that answer. Thanks to all for helping.

CreateProcessW's first parameter has to be either a full path or a path relative to the current directory. It can't be a path relative to the working directory parameter, which seems like what you're expecting it to do.
Try passing C:\Programme\Movie Maker\moviemk.exe as the name parameter

The first parameter lpApplicationName of the CreateProcess function will be used as NULL typically and the second parameter lpCommandLine should contain the command line starting with the program name which you want to start.
Just fry to switch the first and the second parameters which you use currently by the CreateProcessW call.

What is the full path you are entering? Runtime.exec might be quoting the argument internally, and you could be running into this situation:
http://support.microsoft.com/kb/179147
Maybe there is a prefix to the path that exists and is causing it to try to execute a folder or other file?
Try putting quotes around the entire path and see if that helps.

Related

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();

How to get an instance of sun.nio.fs.UnixFileSystem on a Windows machine?

In particular, I'd like to use the (unfortunately not visible) sun.nio.fs.Globs.toUnixRegexPattern(String glob).
Ok, stepping back and giving a bit of context
I have an iterator of pathes into a remote, unix-like file system (think ssh unixhost find path -type f). I also have a user-supplied glob pattern which I now want to match each path against.
On a unix machine, the following works just fine:
matcher = FileSystems.getDefault().getPathMatcher("glob:" + glob);
// ...
for (String s : remoteFind(...)) {
if (matcher.matches(Paths.get(s))) {
// matches, do something
}
}
But when this is run on Windows, the same program totally fails because the FileSystems.getDefault() returns a Windows filesystem (the horror, the horror) and '\' is used as separator, etc. You get the picture. Nothing matches.
Of course I can stop all this nonsense and just rewrite (or rather, copy) sun.nio.fs.Globs.toUnixRegexPattern(String glob), but is there another, more elegant way?
Ok, so just to close this question, as stated in the comments I ended up writing a method in my FileUtil that is almost verbatim a copy of sun.nio.fs.Globs.toUnixRegexPattern(String glob). Works great.
If somebody finds a better way please add a different answer here.
If you do not make any file system operations locally, you could try to set
-Dfile.separator=/
system variable to mimic the unix path separator. This variable should be passed to JVM on startup
As sun.nio.fs.UnixFileSystem is not even part of my Windows JDK, I went one step back and looked for FileSystemProviders that are available on all platforms. So I found JrtFileSystemProvider, which can be (mis-)used to get a Unix-like path matcher on Windows (the following is copy & paste from some Kotlin code, but you get the idea):
val jrtFileSystem = FileSystems.getFileSystem(URI("jrt:/"))
// ...
val pattern = "..."
val matcher = jrtFileSystem.getPathMatcher("glob:$pattern")
// ...
matcher.matches(jrtFileSystem.getPath("path/to/match"))

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