mirror of
https://github.com/unicxrn/prospack.git
synced 2026-10-17 11:01:48 +02:00
Handle ftpsrv reply codes
This commit is contained in:
1 parent
d88425bf13
commit
c5b8bf247f
4 files changed
+46
-10
No files matched your search
@@ -106,11 +106,12 @@ impl Ftp {
|
||||
|
||||
pub fn rename(&mut self, from: &str, to: &str) -> Result<(), String> {
|
||||
self.expect(&format!("RNFR {from}"), &[350])?;
|
||||
self.expect(&format!("RNTO {to}"), &[250]).map(|_| ())
|
||||
// ftpsrv (the usual PS5 server) answers RNTO and DELE with 226 rather than 250.
|
||||
self.expect(&format!("RNTO {to}"), &[250, 226]).map(|_| ())
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, path: &str) -> Result<(), String> {
|
||||
self.expect(&format!("DELE {path}"), &[250]).map(|_| ())
|
||||
self.expect(&format!("DELE {path}"), &[250, 226]).map(|_| ())
|
||||
}
|
||||
|
||||
fn pasv(&mut self) -> Result<TcpStream, String> {
|
||||
@@ -224,6 +225,10 @@ impl Ftp {
|
||||
pub fn put_atomic(&mut self, path: &str, bytes: &[u8]) -> Result<(), String> {
|
||||
let (dir, name) = path.rsplit_once('/').ok_or("bad path")?;
|
||||
let tmp = format!("{dir}/.{name}.packer-tmp");
|
||||
// ftpsrv's STOR doesn't truncate before writing, so never write over a leftover temp file.
|
||||
if self.size(&tmp)?.is_some() {
|
||||
self.delete(&tmp)?;
|
||||
}
|
||||
let never = AtomicBool::new(false);
|
||||
self.store(&tmp, &mut &bytes[..], 0, &never, &mut |_| {})?;
|
||||
self.rename(&tmp, path)
|
||||
|
||||
@@ -119,6 +119,15 @@ pub fn run() {
|
||||
let engine = Arc::new(engine::Engine::new(&settings::data_dir()));
|
||||
let _ = engine.write_verifier();
|
||||
let queue = pipeline::Queue::new(emitter, engine.clone());
|
||||
// Folders passed on the command line (or via "Open with") are queued straight away.
|
||||
for arg in std::env::args().skip(1).filter(|a| !a.starts_with('-')) {
|
||||
let p = PathBuf::from(&arg);
|
||||
if p.is_dir() {
|
||||
if let Err(e) = dump::inspect(&p).and_then(|info| queue.add(info)) {
|
||||
eprintln!("{arg}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
app.manage(App { queue, engine });
|
||||
Ok(())
|
||||
})
|
||||
|
||||
@@ -396,6 +396,7 @@ impl Queue {
|
||||
j.files_total = 0;
|
||||
j.rate = 0.0;
|
||||
j.rate_history.clear();
|
||||
j.rate_label = "";
|
||||
j.eta_secs = None;
|
||||
j.stage_secs = 0;
|
||||
j.log("hl", format!("{} started", title_case(STAGES[stage])));
|
||||
@@ -506,6 +507,11 @@ impl Queue {
|
||||
self.with(id, |j| {
|
||||
j.image_path = Some(final_path.to_string_lossy().into_owned());
|
||||
j.image_bytes = Some(size);
|
||||
if j.dump.apr && !j.dump.has_ampr_index && Path::new(&j.dump.path).join("ampr_emu.index").is_file() {
|
||||
j.dump.has_ampr_index = true;
|
||||
j.warnings.retain(|w| !w.contains("ampr_emu.index"));
|
||||
j.log("", "MkPFS added ampr_emu.index to the dump folder");
|
||||
}
|
||||
let saved = j.dump.total_bytes.saturating_sub(size);
|
||||
j.stages[0].meta = format!("{} → {} · {}", gb(j.dump.total_bytes), gb(size), dur(secs));
|
||||
j.log(
|
||||
@@ -868,14 +874,21 @@ mod e2e {
|
||||
let _ = std::fs::remove_file(dump.join("ampr_emu.index"));
|
||||
|
||||
let ps5_root = tmp.path().join("ps5");
|
||||
let port = 22121;
|
||||
let mut server = std::process::Command::new(scratch.join("mkvenv/bin/python"))
|
||||
let port = 22000 + (std::process::id() % 1000) as u16;
|
||||
struct KillOnDrop(std::process::Child);
|
||||
impl Drop for KillOnDrop {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
let _ = self.0.wait();
|
||||
}
|
||||
}
|
||||
let _server = KillOnDrop(std::process::Command::new(scratch.join("mkvenv/bin/python"))
|
||||
.arg(scratch.join("fake_ps5.py"))
|
||||
.arg(&ps5_root)
|
||||
.arg(port.to_string())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
.unwrap());
|
||||
std::thread::sleep(Duration::from_millis(1500));
|
||||
|
||||
let s = Settings {
|
||||
@@ -948,7 +961,5 @@ mod e2e {
|
||||
assert!(j5.log.iter().any(|l| l.text.starts_with("Resuming at")), "should resume");
|
||||
assert_eq!(std::fs::read(&remote).unwrap(), good, "resumed upload must be byte-identical");
|
||||
assert!(!part.exists());
|
||||
|
||||
server.kill().ok();
|
||||
}
|
||||
}
|
||||
+14
-3
@@ -42,7 +42,11 @@
|
||||
{#if job.status === "running"}
|
||||
<div class="left">
|
||||
<div class="kicker">Step {job.stage + 1} of 4 · {STAGE_NAMES[stageKey]}</div>
|
||||
<div class="big num">{shown.toFixed(1)}<small>%</small></div>
|
||||
{#if stageKey === "mount"}
|
||||
<div class="big num">{clock(job.stageSecs)}<small>of 2:00</small></div>
|
||||
{:else}
|
||||
<div class="big num">{shown.toFixed(1)}<small>%</small></div>
|
||||
{/if}
|
||||
<p class="desc">{description}</p>
|
||||
<div class="bigbar"><i style:width="{job.pct}%"></i></div>
|
||||
<dl class="stats">
|
||||
@@ -73,7 +77,10 @@
|
||||
{#if job.rateLabel}
|
||||
<Sparkline values={job.rateHistory} color={job.dump.accent} />
|
||||
{:else}
|
||||
<div class="idle mono">Polling the PS5 every 3 s</div>
|
||||
<div class="idle">
|
||||
<span class="pulse"></span>
|
||||
<span class="mono">Checking /user/app/{job.dump.titleId}/mount_img.lnk every 3 s</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="actions">
|
||||
@@ -173,7 +180,11 @@
|
||||
.rate small { font-size: 13px; color: var(--muted); margin-left: 4px; }
|
||||
.src { color: var(--faint); }
|
||||
.spark { flex: 1; min-height: 110px; margin: 14px -4px 0; position: relative; }
|
||||
.idle { position: absolute; inset: 0; display: grid; place-items: center; color: var(--faint); }
|
||||
.idle { position: absolute; inset: 0; display: flex; flex-direction: column; gap: 18px; align-items: center; justify-content: center; color: var(--faint); text-align: center; }
|
||||
.pulse { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); position: relative; }
|
||||
.pulse::before, .pulse::after { content: ""; position: absolute; inset: 0; border-radius: 50%; border: 1px solid var(--accent); animation: ring 2.4s var(--ease) infinite; }
|
||||
.pulse::after { animation-delay: 1.2s; }
|
||||
@keyframes ring { from { transform: scale(1); opacity: 0.8; } to { transform: scale(5); opacity: 0; } }
|
||||
.actions { display: flex; gap: 8px; align-items: center; margin-top: 22px; }
|
||||
.saved { margin-left: auto; color: var(--muted); font-size: 12.5px; }
|
||||
|
||||
|
||||
Reference in new issue
Block a user