Skip to content

Commit

Permalink
Write to final file only of no exception during write happened
Browse files Browse the repository at this point in the history
  • Loading branch information
koppor committed May 2, 2022
1 parent 1835958 commit 242e799
Showing 1 changed file with 41 additions and 27 deletions.
68 changes: 41 additions & 27 deletions src/main/java/org/jabref/logic/exporter/AtomicFileOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,18 @@ public class AtomicFileOutputStream extends FilterOutputStream {
* The file to which writes are redirected to.
*/
private final Path temporaryFile;

private final FileLock temporaryFileLock;

/**
* A backup of the target file (if it exists), created when the stream is closed
*/
private final Path backupFile;

private final boolean keepBackup;

private boolean errorDuringWrite = false;

/**
* Creates a new output stream to write to or replace the file at the specified path.
*
Expand Down Expand Up @@ -124,6 +129,7 @@ public void write(byte b[], int off, int len) throws IOException {
out.write(b, off, len);
} catch (IOException exception) {
cleanup();
errorDuringWrite = true;
throw exception;
}
}
Expand Down Expand Up @@ -174,43 +180,50 @@ public void close() throws IOException {
}
super.close();

// We successfully wrote everything to the temporary file, lets copy it to the correct place
// First, make backup of original file and try to save file permissions to restore them later (by default: 664)
Set<PosixFilePermission> oldFilePermissions = EnumSet.of(PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.GROUP_WRITE,
PosixFilePermission.OTHERS_READ);
if (Files.exists(targetFile)) {
Files.copy(targetFile, backupFile, StandardCopyOption.REPLACE_EXISTING);
if (FileUtil.IS_POSIX_COMPILANT) {
try {
oldFilePermissions = Files.getPosixFilePermissions(targetFile);
} catch (IOException exception) {
LOGGER.warn("Error getting file permissions for file {}.", targetFile, exception);
}
}
if (!errorDuringWrite) {
replaceOriginalFileByWrittenFile();
}

// Move temporary file (replace original if it exists)
Files.move(temporaryFile, targetFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
if (!keepBackup) {
// Remove backup file
Files.deleteIfExists(backupFile);
}
} finally {
// Remove temporary file (but not the backup!)
cleanup();
}
}

// Restore file permissions
private void replaceOriginalFileByWrittenFile() throws IOException {
// We successfully wrote everything to the temporary file, lets copy it to the correct place
// First, make backup of original file and try to save file permissions to restore them later (by default: 664)
Set<PosixFilePermission> oldFilePermissions = EnumSet.of(PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.GROUP_WRITE,
PosixFilePermission.OTHERS_READ);
if (Files.exists(targetFile)) {
// Make a backup of the original file
Files.copy(targetFile, backupFile, StandardCopyOption.REPLACE_EXISTING);
if (FileUtil.IS_POSIX_COMPILANT) {
try {
Files.setPosixFilePermissions(targetFile, oldFilePermissions);
oldFilePermissions = Files.getPosixFilePermissions(targetFile);
} catch (IOException exception) {
LOGGER.warn("Error writing file permissions to file {}.", targetFile, exception);
LOGGER.warn("Error getting file permissions for file {}.", targetFile, exception);
}
}
}

if (!keepBackup) {
// Remove backup file
Files.deleteIfExists(backupFile);
// Move temporary file (replace original if it exists)
Files.move(temporaryFile, targetFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);

// Restore file permissions
if (FileUtil.IS_POSIX_COMPILANT) {
try {
Files.setPosixFilePermissions(targetFile, oldFilePermissions);
} catch (IOException exception) {
LOGGER.warn("Error writing file permissions to file {}.", targetFile, exception);
}
} finally {
// Remove temporary file (but not the backup!)
cleanup();
}
}

Expand All @@ -230,6 +243,7 @@ public void write(int b) throws IOException {
super.write(b);
} catch (IOException exception) {
cleanup();
errorDuringWrite = true;
throw exception;
}
}
Expand Down

0 comments on commit 242e799

Please sign in to comment.