mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 02:30:09 +00:00
Preferences: a recovery folder that can't be listed is refused
Data Recovery lists the folders in its recovery folder before it starts, so a folder that can be written but not listed (a Unix folder with mode 0333) left it unable to start. Choose... now lists the picked folder as well as making a folder in it, and refuses it with a status message when either fails. The progress line says what is checked.
This commit is contained in:
@@ -427,17 +427,18 @@ fn choose_recovery_folder(app: &mut VectorcraftApp, d: &mut Dialog) {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Ok` when `path` is a folder that a folder can be created in, as the recovery store makes its
|
||||
/// own there: an empty folder is made there and removed. A folder that can't be removed again is
|
||||
/// an error too.
|
||||
/// `Ok` when `path` is a folder that can be listed and that a folder can be created in, as the
|
||||
/// recovery store lists its folders there and makes its own: the folder is listed, and an empty
|
||||
/// folder is made there and removed. A folder that can't be removed again is an error too.
|
||||
fn writable_folder(path: &str) -> Result<(), String> {
|
||||
let dir = std::path::Path::new(path);
|
||||
if !dir.is_dir() {
|
||||
return Err(format!("{path} is not a folder"));
|
||||
}
|
||||
let refused = |e: std::io::Error| format!("Data Recovery can't keep copies in {path}: {e}");
|
||||
std::fs::read_dir(dir).map_err(refused)?;
|
||||
let nanos = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map_or(0, |t| t.as_nanos());
|
||||
let check = dir.join(format!(".vectorcraft-write-check-{}-{nanos}", std::process::id()));
|
||||
let refused = |e: std::io::Error| format!("Data Recovery can't keep copies in {path}: {e}");
|
||||
std::fs::create_dir(&check).map_err(refused)?;
|
||||
std::fs::remove_dir(&check).map_err(refused)
|
||||
}
|
||||
|
||||
@@ -506,6 +506,20 @@ fn a_recovery_folder_that_cannot_be_written_is_refused() {
|
||||
assert!(a.ui.status.starts_with("Data Recovery can't keep copies in "), "{}", a.ui.status);
|
||||
}
|
||||
let _ = std::fs::set_permissions(&locked, std::fs::Permissions::from_mode(0o755));
|
||||
// A folder that can be written but not listed: the recovery store lists its folders.
|
||||
let unlisted = dir.join("unlisted");
|
||||
std::fs::create_dir(&unlisted).unwrap();
|
||||
std::fs::set_permissions(&unlisted, std::fs::Permissions::from_mode(0o333)).unwrap();
|
||||
// An account that may list anyway (root) has nothing to refuse.
|
||||
if std::fs::read_dir(&unlisted).is_err() {
|
||||
let answer = unlisted.to_string_lossy().into_owned();
|
||||
a.services.pick_folder = Some(Box::new(move || Some(answer.clone())));
|
||||
a.ui.status.clear();
|
||||
choose_recovery_folder(&mut a, &mut d);
|
||||
assert_eq!(d.str("recoveryFolder"), "kept");
|
||||
assert!(a.ui.status.starts_with("Data Recovery can't keep copies in "), "{}", a.ui.status);
|
||||
}
|
||||
let _ = std::fs::set_permissions(&unlisted, std::fs::Permissions::from_mode(0o755));
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
@@ -1134,7 +1134,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
PDF export writes a linear or radial gradient that mixes a spot color with process colors, or two spot colors, as a DeviceN shading of the spot inks and the process inks its stops use, so each spot ink prints on its own plate; an off-center midpoint is written as a stop with the inks halfway between its two stops. A gradient that mixes a spot color with RGB or Lab colors is still written without the spot ink, with a warning. A Perceptual gradient of spot inks is written as a Separation or DeviceN shading of the stops on its curve; a dithered gradient fill, and a Perceptual gradient with RGB or Lab stops other than white or more than 4,096 stops on its curve, are written as images in process colors, with a warning. A Lab spot color in such a shading is named by the same Separation space, with the Lab alternate, as its flat fills (`engine/src/tests/linked_stops.rs`, `engine/src/tests/labspots.rs`).
|
||||
PDF export writes a spot color named All or None (in a Separation space, the names of every plate and of no plate: [ISO 32000-1:2008](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf), 8.6.6.4) as the ink All Spot or None Spot (All Spot 2 and on when a spot swatch already has that name) in its fills, strokes, gradients, colorized images and Lab alternates; Registration still prints on every plate (`pdf/src/tests/output.rs`).
|
||||
- `.vectorcraft` (lossless JSON, compressed, atomic saves, save down to v1), Save As with a format chooser, Save a Copy, Revert, templates, Data Recovery after a crash (browser tabs hold their copies with Web Locks too, so a background tab's copies are never taken for a crash's, and a tab repairs copies another removed when it resumes: #367), background save and export. The desktop app writes its warnings and errors (graphics adapter, a lost graphics device, panics the guard recovered from) to standard error and to `logs/vectorcraft.log` next to the preferences, the two previous runs kept and each file capped at 16 MiB, so a launch without a terminal still leaves something to attach to a bug report (#465). Native documents with a lot of path data (traced or generated art, in layers, symbols or patterns) are saved as compact JSON, about a fifth of the size; small documents stay pretty-printed (#1492). Saving to a network drive on macOS falls back to a plain fsync when the share refuses a full flush (#1625, part of #1620; not confirmed on a real share), and a save still succeeds when a folder refuses the temporary file, the rename over the document or copying permissions: the content is written elsewhere first and copied in, and a failed copy keeps the temporary file and names it (#1649, part of #1620). A document opened by a relative path finds its relatively linked images, and `vectorcraft-cli convert` lists what reading the input left out as `importWarnings` (#1606, fixes #1604).
|
||||
Preferences › File Handling › Data Recovery › Folder has a Choose… button on the desktop that picks the folder Data Recovery keeps its copies in ([Illustrator reference, Set Data Recovery preferences](https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf)); a picked folder that a file cannot be created in is refused with a status message, and the field keeps its value.
|
||||
Preferences › File Handling › Data Recovery › Folder has a Choose… button on the desktop that picks the folder Data Recovery keeps its copies in ([Illustrator reference, Set Data Recovery preferences](https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf)); a picked folder that Data Recovery cannot create its folders in or list is refused with a status message, and the field keeps its value.
|
||||
A name typed in the desktop save panel without the file type's extension gets it, and when a file of that name exists, which the panel did not ask about, Save, Save As, Save a Copy, Save as Template, the exports and every other write through a save panel ask before replacing it (dialog `replaceFile`; Cancel writes nothing).
|
||||
A name returned by the desktop save panel with the extension of a format the save does not write has it replaced with the saved format's: Save As of a new document saves `card.ait` and `card.png` as `card.vectorcraft`, and Save as Template saves `card.pdf` as `card.vctemplate`. An extension that names no format stays part of the name (`notes.v2` is saved as `notes.v2.vectorcraft`).
|
||||
Export As, Save as PDF, Save for Office Documents, Save for Web, Text Export Options, print to file, the presets' Export… and the library, swatch and Document Info saves replace a typed extension of another format than the one they write in the same way: Export As PNG writes `card.svg` as `card.png`, Save as PDF writes `card.png` as `card.pdf`, and `document.exportSelection` with a `format` suggests and adds that format's extension. An extension of the written format stays as typed in any letter case (`photo.JPEG` for JPEG), Export As SVG writes `art.svgz` compressed, and `document.exportSelection` without a `format` writes the format the typed extension names (`tests/saveext.rs`).
|
||||
|
||||
Reference in New Issue
Block a user