Changeset 19179 in josm for trunk/src/org


Ignore:
Timestamp:
2024-08-12T17:04:53+02:00 (3 months ago)
Author:
taylor.smock
Message:

Fix #23802: Backup preferences files get overwritten by bad preference files

This validates a preferences file before copying/moving it to another file.
This should avoid cases where a file is partially written and then copied or
moved over a "good" preferences file.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r19121 r19179  
    440440        // Backup old preferences if there are old preferences
    441441        if (initSuccessful && prefFile.exists() && prefFile.length() > 0) {
    442             Utils.copyFile(prefFile, backupFile);
     442            checkFileValidity(prefFile, f -> Utils.copyFile(f, backupFile));
    443443        }
    444444
     
    451451
    452452        File tmpFile = new File(prefFile + "_tmp");
    453         Files.move(tmpFile.toPath(), prefFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
     453        // Only replace the pref file if the _tmp file is valid
     454        checkFileValidity(tmpFile, f -> Files.move(f.toPath(), prefFile.toPath(), StandardCopyOption.REPLACE_EXISTING));
    454455
    455456        setCorrectPermissions(prefFile);
    456457        setCorrectPermissions(backupFile);
     458    }
     459
     460    /**
     461     * Ensure that a preferences file is "ok" before copying/moving it over another preferences file
     462     * @param file The file to check
     463     * @param consumer The consumer that will perform the copy/move action
     464     * @throws IOException If there is an issue reading/writing the file
     465     */
     466    private static void checkFileValidity(File file, ThrowingConsumer<File, IOException> consumer) throws IOException {
     467        try {
     468            // But don't back up if the current preferences are invalid.
     469            // The validations are expensive (~2/3 CPU, ~1/3 memory), but this isn't a "hot" method
     470            PreferencesReader.validateXML(file);
     471            PreferencesReader reader = new PreferencesReader(file, false);
     472            reader.parse();
     473            consumer.accept(file);
     474        } catch (SAXException | XMLStreamException e) {
     475            Logging.trace(e);
     476            Logging.debug("Invalid preferences file (" + file + ") due to: " + e.getMessage());
     477        }
    457478    }
    458479
     
    974995        }
    975996    }
     997
     998    /**
     999     * A consumer that can throw an exception
     1000     * @param <T> The object type to accept
     1001     * @param <E> The throwable type
     1002     */
     1003    private interface ThrowingConsumer<T, E extends Throwable> {
     1004        /**
     1005         * Accept an object
     1006         * @param object The object to accept
     1007         * @throws E The exception that can be thrown
     1008         */
     1009        void accept(T object) throws E;
     1010    }
    9761011}
Note: See TracChangeset for help on using the changeset viewer.