mirror of
https://github.com/LisherSong/ps5-web-file-manager.git
synced 2026-10-06 07:00:28 +02:00
Fixed PKG install function for game base PKG
This commit is contained in:
1 parent
bd4d1c45a6
commit
34de7d2483
6 files changed
+156
-48
No files matched your search
+45
-13
@@ -30,6 +30,7 @@ let permissionOriginal = "";
|
||||
let permissionBusy = false;
|
||||
let downloadFrame = null;
|
||||
let uploadXhr = null;
|
||||
let uploadTerminalAbort = false;
|
||||
let L = {};
|
||||
|
||||
const APP_VERSION = "v1.6";
|
||||
@@ -458,6 +459,10 @@ function clearTrackedTask() {
|
||||
downloadFrame.parentNode.removeChild(downloadFrame);
|
||||
downloadFrame = null;
|
||||
}
|
||||
if (trackedTask && trackedTask.op === "upload" && uploadXhr) {
|
||||
uploadTerminalAbort = true;
|
||||
uploadXhr.abort();
|
||||
}
|
||||
trackedTask = null;
|
||||
}
|
||||
|
||||
@@ -1798,8 +1803,11 @@ function renderTasks(tasks) {
|
||||
if (confirm(t("cancelTaskConfirm", { label: opLabel(task.op) }))) {
|
||||
requestTaskCancel(task.id);
|
||||
api("/api/cancel", { id: task.id }).then(() => {
|
||||
if (task.op === "upload" && uploadXhr) uploadXhr.abort();
|
||||
return pollTasks();
|
||||
if (task.op !== "upload") return pollTasks();
|
||||
if (uploadXhr) uploadXhr.abort();
|
||||
return api("/api/upload/finish", { task_id: task.id })
|
||||
.catch(() => {})
|
||||
.then(pollTasks);
|
||||
}).catch(err => {
|
||||
if (trackedTask && trackedTask.id === task.id) trackedTask.cancelRequested = false;
|
||||
setStatus(t("cancelFailed", { error: err.message }));
|
||||
@@ -1941,7 +1949,7 @@ function uploadConflicts(rels) {
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
function uploadFileRequest(taskId, file, rel, overwrite) {
|
||||
function uploadFileRequest(taskId, file, rel, overwrite, index) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
uploadXhr = xhr;
|
||||
@@ -1967,12 +1975,25 @@ function uploadFileRequest(taskId, file, rel, overwrite) {
|
||||
};
|
||||
xhr.onerror = () => {
|
||||
uploadXhr = null;
|
||||
reject(new Error(xhr.statusText || t("backendError")));
|
||||
const error = new Error(xhr.statusText || t("backendError"));
|
||||
api("/api/tasks").then(data => {
|
||||
const task = (data.tasks || []).find(item => item.id === taskId);
|
||||
if (task && Number(task.completed_count || 0) >= index) {
|
||||
resolve();
|
||||
} else if (task && task.state === "failed") {
|
||||
reject(new Error(backendErrorText(task.error_code, task.error_arg,
|
||||
task.error || task.current)));
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
}).catch(() => reject(error));
|
||||
};
|
||||
xhr.onabort = () => {
|
||||
uploadXhr = null;
|
||||
const terminal = uploadTerminalAbort;
|
||||
uploadTerminalAbort = false;
|
||||
const err = new Error("aborted");
|
||||
err.name = "AbortError";
|
||||
err.name = terminal ? "TerminalTaskAbort" : "AbortError";
|
||||
reject(err);
|
||||
};
|
||||
xhr.send(file);
|
||||
@@ -2030,21 +2051,32 @@ async function uploadFiles(files) {
|
||||
const file = list[i];
|
||||
const rel = rels[i];
|
||||
setStatus(t("uploadingStatus", { index: i + 1, count: list.length, name: rel }));
|
||||
await uploadFileRequest(taskId, file, rel, overwrite);
|
||||
await uploadFileRequest(taskId, file, rel, overwrite, i + 1);
|
||||
}
|
||||
await api("/api/upload/finish", { task_id: taskId });
|
||||
await pollTasks();
|
||||
setStatus(t("uploadDone", { count: list.length }));
|
||||
} catch (err) {
|
||||
const terminalAbort = err && err.name === "TerminalTaskAbort";
|
||||
const canceled = err && err.name === "AbortError";
|
||||
if (canceled) {
|
||||
setStatus(t("taskCanceled", { label: opLabel("upload") }));
|
||||
} else {
|
||||
const message = t("uploadFailed", { error: err.message });
|
||||
setStatus(message);
|
||||
alert(message);
|
||||
if (!terminalAbort) {
|
||||
if (taskId) {
|
||||
try {
|
||||
await api("/api/cancel", { id: taskId });
|
||||
await api("/api/upload/finish", { task_id: taskId });
|
||||
} catch (cancelErr) {
|
||||
/* The server may already have moved a failed upload to a terminal state. */
|
||||
}
|
||||
}
|
||||
await pollTasks();
|
||||
if (canceled) {
|
||||
setStatus(t("taskCanceled", { label: opLabel("upload") }));
|
||||
} else {
|
||||
const message = t("uploadFailed", { error: err.message });
|
||||
setStatus(message);
|
||||
alert(message);
|
||||
}
|
||||
}
|
||||
await pollTasks();
|
||||
} finally {
|
||||
uploadXhr = null;
|
||||
uploadFilesEl.value = "";
|
||||
|
||||
+3
-2
@@ -1853,8 +1853,9 @@ api_tasks(struct MHD_Connection *conn) {
|
||||
json_escape(&b, task->error_code);
|
||||
strbuf_append(&b, ",\"error_arg\":");
|
||||
json_escape(&b, task->error_arg);
|
||||
strbuf_printf(&b, ",\"src_count\":%zu,\"total\":%llu,\"done\":%llu,\"speed\":%llu,\"eta\":%llu,\"cancel_requested\":%s,\"created_at\":%lld,\"elapsed\":%lld,\"total_elapsed\":%lld,\"updated_at\":%lld}",
|
||||
task->src_count, task->total, task->done, task->speed, task->eta,
|
||||
strbuf_printf(&b, ",\"src_count\":%zu,\"completed_count\":%zu,\"total\":%llu,\"done\":%llu,\"speed\":%llu,\"eta\":%llu,\"cancel_requested\":%s,\"created_at\":%lld,\"elapsed\":%lld,\"total_elapsed\":%lld,\"updated_at\":%lld}",
|
||||
task->src_count, task->upload_completed,
|
||||
task->total, task->done, task->speed, task->eta,
|
||||
task->cancel_requested ? "true" : "false",
|
||||
(long long)task->created_at,
|
||||
task->transfer_started_at ? (long long)(now - task->transfer_started_at) : 0LL,
|
||||
|
||||
@@ -45,6 +45,7 @@ typedef struct file_task {
|
||||
size_t src_count;
|
||||
size_t file_count;
|
||||
size_t dir_count;
|
||||
size_t upload_completed;
|
||||
unsigned int chmod_mode;
|
||||
int recursive;
|
||||
unsigned long long total;
|
||||
@@ -57,6 +58,7 @@ typedef struct file_task {
|
||||
unsigned int eta_sample_next;
|
||||
unsigned int eta_sample_count;
|
||||
int cancel_requested;
|
||||
unsigned int active_streams;
|
||||
time_t created_at;
|
||||
time_t transfer_started_at;
|
||||
time_t updated_at;
|
||||
|
||||
+15
-4
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -14,8 +15,13 @@ typedef struct pkg_metadata {
|
||||
const char *content_id;
|
||||
const char *content_name;
|
||||
const char *icon_url;
|
||||
uint32_t slot;
|
||||
uint32_t is_playgo_enabled;
|
||||
} pkg_metadata_t;
|
||||
|
||||
_Static_assert(sizeof(pkg_metadata_t) == 0x38,
|
||||
"sceAppInstUtil metadata ABI mismatch");
|
||||
|
||||
typedef struct pkg_info {
|
||||
char content_id[48];
|
||||
int type;
|
||||
@@ -23,12 +29,15 @@ typedef struct pkg_info {
|
||||
} pkg_info_t;
|
||||
|
||||
typedef struct playgo_info {
|
||||
char lang[8][30];
|
||||
char scenario_ids[3][64];
|
||||
char content_ids[64];
|
||||
char languages[30][8];
|
||||
char scenario_ids[64][3];
|
||||
char content_ids[64][48];
|
||||
long unknown[810];
|
||||
} playgo_info_t;
|
||||
|
||||
_Static_assert(sizeof(playgo_info_t) == 0x2700,
|
||||
"sceAppInstUtil PlayGoInfo ABI mismatch");
|
||||
|
||||
int sceAppInstUtilInitialize(void);
|
||||
int sceAppInstUtilInstallByPackage(const pkg_metadata_t *, pkg_info_t *,
|
||||
playgo_info_t *);
|
||||
@@ -68,7 +77,9 @@ pkg_installer_install(const char *path) {
|
||||
.playgo_scenario_id = "",
|
||||
.content_id = "",
|
||||
.content_name = "",
|
||||
.icon_url = ""
|
||||
.icon_url = "",
|
||||
.slot = 0,
|
||||
.is_playgo_enabled = 0
|
||||
};
|
||||
pkg_info_t pkg_info = {0};
|
||||
playgo_info_t playgo_info = {0};
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ remove_finished_tasks_locked(void) {
|
||||
while(*link) {
|
||||
file_task_t *task = *link;
|
||||
|
||||
if(task_is_active(task)) {
|
||||
if(task_is_active(task) || task->active_streams) {
|
||||
link = &task->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
+90
-28
@@ -28,8 +28,22 @@ typedef struct upload_context {
|
||||
int failed;
|
||||
int error;
|
||||
int task_done;
|
||||
const char *stage;
|
||||
char error_message[256];
|
||||
} upload_context_t;
|
||||
|
||||
static const char *
|
||||
upload_error_message(upload_context_t *ctx) {
|
||||
if(!ctx->error_message[0]) {
|
||||
snprintf(ctx->error_message, sizeof(ctx->error_message), "%s failed%s%s: %s",
|
||||
ctx->stage ? ctx->stage : "upload",
|
||||
ctx->target[0] ? " for " : "",
|
||||
ctx->target[0] ? ctx->target : "",
|
||||
strerror(ctx->error ? ctx->error : EIO));
|
||||
}
|
||||
return ctx->error_message;
|
||||
}
|
||||
|
||||
static int
|
||||
upload_flush(upload_context_t *ctx) {
|
||||
size_t written = 0;
|
||||
@@ -81,11 +95,29 @@ finish_upload_task(file_task_t *task, task_state_t state, const char *current,
|
||||
if(error) {
|
||||
snprintf(task->error, sizeof(task->error), "%s", error);
|
||||
}
|
||||
if(state == TASK_FAILED) {
|
||||
snprintf(task->error_code, sizeof(task->error_code), "upload_failed");
|
||||
snprintf(task->error_arg, sizeof(task->error_arg), "%s",
|
||||
current ? current : task->src);
|
||||
}
|
||||
task->updated_at = completed_at;
|
||||
}
|
||||
pthread_mutex_unlock(&g_tasks_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
finish_upload_context_error(upload_context_t *ctx) {
|
||||
if(!ctx->task || ctx->task_done) {
|
||||
return;
|
||||
}
|
||||
upload_error_message(ctx);
|
||||
finish_upload_task(ctx->task,
|
||||
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
|
||||
ctx->target[0] ? ctx->target : ctx->task->current,
|
||||
ctx->error == ECANCELED ? "canceled" : ctx->error_message);
|
||||
ctx->task_done = 1;
|
||||
}
|
||||
|
||||
static file_task_t *
|
||||
upload_task_from_conn(struct MHD_Connection *conn) {
|
||||
char *idstr = request_value(conn, "X-WFM-Task-ID", "task_id");
|
||||
@@ -300,12 +332,18 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
|
||||
return -1;
|
||||
}
|
||||
ctx->fd = -1;
|
||||
ctx->stage = "preparing upload";
|
||||
*upload_ctx = ctx;
|
||||
|
||||
if(size_text) {
|
||||
ctx->expected = strtoull(size_text, NULL, 10);
|
||||
}
|
||||
ctx->task = task;
|
||||
if(task) {
|
||||
pthread_mutex_lock(&g_tasks_lock);
|
||||
task->active_streams++;
|
||||
pthread_mutex_unlock(&g_tasks_lock);
|
||||
}
|
||||
if(task && task_cancel_requested(task)) {
|
||||
ctx->failed = 1;
|
||||
ctx->error = ECANCELED;
|
||||
@@ -316,17 +354,20 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
|
||||
ctx->error = EBUSY;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "validating target path";
|
||||
if(!base || !rel || path_join_relative(ctx->target, sizeof(ctx->target),
|
||||
base, rel)) {
|
||||
ctx->failed = 1;
|
||||
ctx->error = errno ? errno : EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "creating target folders";
|
||||
if(ensure_parent_dirs(base, rel)) {
|
||||
ctx->failed = 1;
|
||||
ctx->error = errno ? errno : EACCES;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "checking target path";
|
||||
if(!lstat(ctx->target, &st)) {
|
||||
if(S_ISDIR(st.st_mode) || !overwrite || strcmp(overwrite, "1")) {
|
||||
ctx->failed = 1;
|
||||
@@ -338,19 +379,26 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
|
||||
ctx->error = errno;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "checking target permissions";
|
||||
if(check_target_writable(ctx->target, &checked_dirs, &checked_dir_count,
|
||||
error, sizeof(error), code, sizeof(code),
|
||||
arg, sizeof(arg))) {
|
||||
snprintf(ctx->error_message, sizeof(ctx->error_message), "%s",
|
||||
error[0] ? error : "target is not writable");
|
||||
ctx->failed = 1;
|
||||
ctx->error = errno ? errno : EACCES;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "checking target space";
|
||||
if(check_target_space(ctx->target, ctx->expected, error, sizeof(error),
|
||||
code, sizeof(code), arg, sizeof(arg))) {
|
||||
snprintf(ctx->error_message, sizeof(ctx->error_message), "%s",
|
||||
error[0] ? error : "not enough target space");
|
||||
ctx->failed = 1;
|
||||
ctx->error = errno ? errno : ENOSPC;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "creating temporary path";
|
||||
n = snprintf(ctx->temp, sizeof(ctx->temp), "%s.wfm-upload-%ld-%lld.tmp",
|
||||
ctx->target, (long)getpid(), (long long)time(NULL));
|
||||
if(n < 0 || (size_t)n >= sizeof(ctx->temp)) {
|
||||
@@ -358,12 +406,14 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
|
||||
ctx->error = ENAMETOOLONG;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "opening temporary file";
|
||||
ctx->fd = open(ctx->temp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
if(ctx->fd < 0) {
|
||||
ctx->failed = 1;
|
||||
ctx->error = errno;
|
||||
goto fail;
|
||||
}
|
||||
ctx->stage = "allocating upload buffer";
|
||||
if(!(ctx->buffer = malloc(UPLOAD_BUFFER_SIZE))) {
|
||||
ctx->failed = 1;
|
||||
ctx->error = ENOMEM;
|
||||
@@ -374,13 +424,7 @@ fail:
|
||||
free_paths(checked_dirs, checked_dir_count);
|
||||
free(base); free(rel); free(overwrite); free(size_text);
|
||||
if(ctx->failed) {
|
||||
if(ctx->task) {
|
||||
finish_upload_task(ctx->task,
|
||||
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
|
||||
ctx->target[0] ? ctx->target : ctx->task->current,
|
||||
ctx->error == ECANCELED ? "canceled" : strerror(ctx->error ? ctx->error : EIO));
|
||||
ctx->task_done = 1;
|
||||
}
|
||||
finish_upload_context_error(ctx);
|
||||
if(ctx->fd >= 0) {
|
||||
close(ctx->fd);
|
||||
ctx->fd = -1;
|
||||
@@ -391,6 +435,7 @@ fail:
|
||||
errno = ctx->error ? ctx->error : EIO;
|
||||
return -1;
|
||||
}
|
||||
ctx->stage = "writing file";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -404,6 +449,7 @@ filemgr_upload_data(void *upload_ctx, const char *data, size_t size) {
|
||||
if(ctx->task && task_cancel_requested(ctx->task)) {
|
||||
ctx->failed = 1;
|
||||
ctx->error = ECANCELED;
|
||||
finish_upload_context_error(ctx);
|
||||
return -1;
|
||||
}
|
||||
while(size) {
|
||||
@@ -415,6 +461,7 @@ filemgr_upload_data(void *upload_ctx, const char *data, size_t size) {
|
||||
data += take;
|
||||
size -= take;
|
||||
if(ctx->buffered == UPLOAD_BUFFER_SIZE && upload_flush(ctx)) {
|
||||
finish_upload_context_error(ctx);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -430,46 +477,55 @@ filemgr_upload_finish(struct MHD_Connection *conn, void *upload_ctx) {
|
||||
return send_json_error(conn, MHD_HTTP_BAD_REQUEST, "invalid path");
|
||||
}
|
||||
if(ctx->failed) {
|
||||
if(ctx->task && !ctx->task_done) {
|
||||
finish_upload_task(ctx->task,
|
||||
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
|
||||
ctx->target[0] ? ctx->target : ctx->task->current,
|
||||
ctx->error == ECANCELED ? "canceled" : strerror(ctx->error ? ctx->error : EIO));
|
||||
ctx->task_done = 1;
|
||||
}
|
||||
finish_upload_context_error(ctx);
|
||||
errno = ctx->error ? ctx->error : EIO;
|
||||
return send_json_error(conn, errno == EEXIST ? MHD_HTTP_CONFLICT :
|
||||
errno == EBUSY ? MHD_HTTP_CONFLICT :
|
||||
errno == ECANCELED ? MHD_HTTP_CONFLICT :
|
||||
errno == ENOSPC ? MHD_HTTP_INSUFFICIENT_STORAGE :
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR, NULL);
|
||||
return send_json_error_detail(conn,
|
||||
errno == EEXIST ? MHD_HTTP_CONFLICT :
|
||||
errno == EBUSY ? MHD_HTTP_CONFLICT :
|
||||
errno == ECANCELED ? MHD_HTTP_CONFLICT :
|
||||
errno == ENOSPC ? MHD_HTTP_INSUFFICIENT_STORAGE :
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
ctx->error == ECANCELED ? "canceled" : ctx->error_message,
|
||||
ctx->error == ECANCELED ? NULL : "upload_failed",
|
||||
ctx->target[0] ? ctx->target : NULL);
|
||||
}
|
||||
ctx->stage = "writing file";
|
||||
if(ctx->buffered && upload_flush(ctx)) {
|
||||
ctx->error = ctx->error ? ctx->error : EIO;
|
||||
goto done;
|
||||
}
|
||||
ctx->stage = "verifying uploaded size";
|
||||
if(ctx->expected && ctx->written != ctx->expected) {
|
||||
ctx->error = EIO;
|
||||
goto done;
|
||||
}
|
||||
ctx->stage = "setting file permissions";
|
||||
if(fchmod_0777(ctx->fd)) {
|
||||
ctx->error = errno;
|
||||
goto done;
|
||||
}
|
||||
ctx->stage = "syncing file data";
|
||||
if(fsync(ctx->fd)) {
|
||||
ctx->error = errno;
|
||||
goto done;
|
||||
}
|
||||
ctx->stage = "closing temporary file";
|
||||
if(close(ctx->fd)) {
|
||||
ctx->fd = -1;
|
||||
ctx->error = errno;
|
||||
goto done;
|
||||
}
|
||||
ctx->fd = -1;
|
||||
ctx->stage = "moving temporary file into place";
|
||||
if(rename(ctx->temp, ctx->target)) {
|
||||
ctx->error = errno;
|
||||
goto done;
|
||||
}
|
||||
if(ctx->task) {
|
||||
pthread_mutex_lock(&g_tasks_lock);
|
||||
ctx->task->upload_completed++;
|
||||
pthread_mutex_unlock(&g_tasks_lock);
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
@@ -478,19 +534,18 @@ done:
|
||||
ctx->fd = -1;
|
||||
}
|
||||
if(ret) {
|
||||
upload_error_message(ctx);
|
||||
if(ctx->temp[0]) {
|
||||
unlink(ctx->temp);
|
||||
}
|
||||
if(ctx->task && !ctx->task_done) {
|
||||
finish_upload_task(ctx->task,
|
||||
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
|
||||
ctx->target[0] ? ctx->target : ctx->task->current,
|
||||
ctx->error == ECANCELED ? "canceled" : strerror(ctx->error ? ctx->error : EIO));
|
||||
ctx->task_done = 1;
|
||||
}
|
||||
finish_upload_context_error(ctx);
|
||||
errno = ctx->error ? ctx->error : EIO;
|
||||
return send_json_error(conn, errno == ECANCELED ? MHD_HTTP_CONFLICT :
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR, NULL);
|
||||
return send_json_error_detail(conn,
|
||||
errno == ECANCELED ? MHD_HTTP_CONFLICT :
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
ctx->error == ECANCELED ? "canceled" : ctx->error_message,
|
||||
ctx->error == ECANCELED ? NULL : "upload_failed",
|
||||
ctx->target[0] ? ctx->target : NULL);
|
||||
}
|
||||
return send_json_ok(conn);
|
||||
}
|
||||
@@ -516,5 +571,12 @@ filemgr_upload_free(void *upload_ctx) {
|
||||
}
|
||||
}
|
||||
free(ctx->buffer);
|
||||
if(ctx->task) {
|
||||
pthread_mutex_lock(&g_tasks_lock);
|
||||
if(ctx->task->active_streams) {
|
||||
ctx->task->active_streams--;
|
||||
}
|
||||
pthread_mutex_unlock(&g_tasks_lock);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
Reference in new issue
Block a user