package org.graalvm.component.installer.os;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclEntryType;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import org.graalvm.component.installer.FileOperations;
public class WindowsFileOperations extends FileOperations {
private Path delayDeletedList;
private Path copyContents;
private Map<Path, Path> copiedPaths = new HashMap<>();
private NavigableSet<Path> delayDeletedPaths = new TreeSet<>();
public void setDelayDeletedList(Path delayDeletedList) {
this.delayDeletedList = delayDeletedList;
}
public void setCopyContents(Path copyContents) {
this.copyContents = copyContents;
}
public Map<Path, Path> getCopiedPaths() {
return copiedPaths;
}
public Set<Path> getDelayDeletedPaths() {
return delayDeletedPaths;
}
@Override
protected boolean doWithPermissions(Path p, Callable<Void> action) throws IOException {
AclFileAttributeView aclView = Files.getFileAttributeView(p, AclFileAttributeView.class);
UserPrincipalLookupService upls = p.getFileSystem().getUserPrincipalLookupService();
String un = System.getProperty("user.name");
UserPrincipal up;
List<AclEntry> save;
try {
up = upls.lookupPrincipalByName(un);
save = aclView.getAcl();
List<AclEntry> temp = new ArrayList<>(save);
AclEntry en = AclEntry.newBuilder().setType(AclEntryType.ALLOW).setPrincipal(up).setPermissions(AclEntryPermission.DELETE).build();
temp.add(en);
aclView.setAcl(temp);
} catch (IOException ex) {
return false;
}
boolean ok = false;
try {
action.call();
ok = true;
} catch (IOException ex) {
throw ex;
} catch (Exception ex) {
ok = false;
} finally {
try {
aclView.setAcl(save);
} catch (IOException ex) {
feedback().error("FILE_ErrorRestoringPermissions", ex, p, ex.getLocalizedMessage());
ok = false;
}
}
return ok;
}
@Override
public boolean flush() throws IOException {
boolean r = false;
if (copyContents != null) {
List<String> lines = new ArrayList<>(copiedPaths.size());
for (Map.Entry<Path, Path> e : copiedPaths.entrySet()) {
Path orig = e.getKey();
if (Files.exists(orig)) {
String s = orig.toAbsolutePath().toString() + "|" + e.getValue().toAbsolutePath().toString();
lines.add(s);
delayDeletedPaths.remove(orig);
}
}
if (!lines.isEmpty()) {
r = true;
}
Files.write(copyContents, lines,
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING);
}
if (delayDeletedList != null) {
List<String> lines = new ArrayList<>(delayDeletedPaths.size());
for (Path p : delayDeletedPaths) {
lines.add(p.toAbsolutePath().toString());
}
if (!lines.isEmpty()) {
r = true;
}
Files.write(delayDeletedList, lines,
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING);
}
return r;
}
@Override
protected void handleUndeletableFile(IOException ex, Path p) throws IOException {
if (delayDeletedList == null) {
super.handleUndeletableFile(ex, p);
return;
}
delayDeletedPaths.add(p);
feedback().output("FILE_CannotDeleteFileTryDelayed", p, ex.getLocalizedMessage());
}
@Override
protected Path handleUnmodifiableFile(IOException ex, Path p, InputStream content) throws IOException {
if (copyContents == null) {
return super.handleUnmodifiableFile(ex, p, content);
}
Path fn = p.getFileName();
Path parentDir = p.getParent();
assert parentDir != null;
assert fn != null;
Path pn = parentDir.getFileName();
assert pn != null;
Path copy = parentDir.resolveSibling(pn.toString() + ".new");
copiedPaths.put(parentDir, copy);
feedback().output("FILE_CannotInstallFileTryDelayed", p, ex.getLocalizedMessage());
Files.createDirectories(copy);
Path target = copy.resolve(fn);
Files.copy(content, target, StandardCopyOption.REPLACE_EXISTING);
return target;
}
@Override
public Path materialize(Path p, boolean write) {
if (copyContents == null || delayDeletedList == null) {
return super.materialize(p, write);
}
Path parentDir = p.getParent();
Path copy = copiedPaths.get(parentDir);
Path fn = p.getFileName();
assert fn != null;
assert parentDir != null;
if (copy != null) {
Path r = copy.resolve(fn);
return r;
}
if (delayDeletedPaths.contains(p)) {
if (write) {
Path pn = parentDir.getFileName();
assert pn != null;
copy = parentDir.resolveSibling(pn.toString() + ".new");
copiedPaths.put(parentDir, copy);
return copy.resolve(fn);
} else {
return null;
}
}
return p;
}
@Override
public void setPermissions(Path target, Set<PosixFilePermission> perms) throws IOException {
}
@Override
protected void performDelete(Path p) throws IOException {
Path next = delayDeletedPaths.ceiling(p);
if (next != null && next.startsWith(p)) {
feedback().output("FILE_CannotDeleteParentTryDelayed", p);
} else {
super.performDelete(p);
}
}
}