mirror of
https://github.com/rclone/rclone.git
synced 2025-08-25 12:25:42 +02:00
pikpak: add file name integrity check during upload
This commit introduces a new validation step to ensure data integrity during file uploads. - The API's returned file name (new.File.Name) is now verified against the requested file name (leaf) immediately after the initial upload ticket is created. - If a mismatch is detected, the upload process is aborted with an error, and the defer cleanup logic is triggered to delete any partially created file. - This addresses an unexpected API behavior where numbered suffixes might be appended to filenames even without conflicts. - This change prevents corrupted or misnamed files from being uploaded without client-side awareness.
This commit is contained in:
@@ -1508,8 +1508,30 @@ func (f *Fs) upload(ctx context.Context, in io.Reader, leaf, dirID, gcid string,
|
||||
}
|
||||
if new.File == nil {
|
||||
return nil, fmt.Errorf("invalid response: %+v", new)
|
||||
} else if new.File.Phase == api.PhaseTypeComplete {
|
||||
// early return; in case of zero-byte objects
|
||||
}
|
||||
|
||||
defer atexit.OnError(&err, func() {
|
||||
fs.Debugf(leaf, "canceling upload: %v", err)
|
||||
if cancelErr := f.deleteObjects(ctx, []string{new.File.ID}, false); cancelErr != nil {
|
||||
fs.Logf(leaf, "failed to cancel upload: %v", cancelErr)
|
||||
}
|
||||
if new.Task != nil {
|
||||
if cancelErr := f.deleteTask(ctx, new.Task.ID, false); cancelErr != nil {
|
||||
fs.Logf(leaf, "failed to cancel upload: %v", cancelErr)
|
||||
}
|
||||
fs.Debugf(leaf, "waiting %v for the cancellation to be effective", taskWaitTime)
|
||||
time.Sleep(taskWaitTime)
|
||||
}
|
||||
})()
|
||||
|
||||
// Note: The API might automatically append a numbered suffix to the filename,
|
||||
// even if a file with the same name does not exist in the target directory.
|
||||
if upName := f.opt.Enc.ToStandardName(new.File.Name); leaf != upName {
|
||||
return nil, fserrors.NoRetryError(fmt.Errorf("uploaded file name mismatch: expected %q, got %q", leaf, upName))
|
||||
}
|
||||
|
||||
// early return; in case of zero-byte objects or uploaded by matched gcid
|
||||
if new.File.Phase == api.PhaseTypeComplete {
|
||||
if acc, ok := in.(*accounting.Account); ok && acc != nil {
|
||||
// if `in io.Reader` is still in type of `*accounting.Account` (meaning that it is unused)
|
||||
// it is considered as a server side copy as no incoming/outgoing traffic occur at all
|
||||
@@ -1519,18 +1541,6 @@ func (f *Fs) upload(ctx context.Context, in io.Reader, leaf, dirID, gcid string,
|
||||
return new.File, nil
|
||||
}
|
||||
|
||||
defer atexit.OnError(&err, func() {
|
||||
fs.Debugf(leaf, "canceling upload: %v", err)
|
||||
if cancelErr := f.deleteObjects(ctx, []string{new.File.ID}, false); cancelErr != nil {
|
||||
fs.Logf(leaf, "failed to cancel upload: %v", cancelErr)
|
||||
}
|
||||
if cancelErr := f.deleteTask(ctx, new.Task.ID, false); cancelErr != nil {
|
||||
fs.Logf(leaf, "failed to cancel upload: %v", cancelErr)
|
||||
}
|
||||
fs.Debugf(leaf, "waiting %v for the cancellation to be effective", taskWaitTime)
|
||||
time.Sleep(taskWaitTime)
|
||||
})()
|
||||
|
||||
if uploadType == api.UploadTypeForm && new.Form != nil {
|
||||
err = f.uploadByForm(ctx, in, req.Name, size, new.Form, options...)
|
||||
} else if uploadType == api.UploadTypeResumable && new.Resumable != nil {
|
||||
@@ -1542,6 +1552,9 @@ func (f *Fs) upload(ctx context.Context, in io.Reader, leaf, dirID, gcid string,
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload: %w", err)
|
||||
}
|
||||
if new.Task == nil {
|
||||
return new.File, nil
|
||||
}
|
||||
return new.File, f.waitTask(ctx, new.Task.ID)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user