Update updates an existing file or creates a new one
(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption)
| 870 | |
| 871 | // Update updates an existing file or creates a new one |
| 872 | func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error { |
| 873 | remote := o.remote |
| 874 | |
| 875 | origBaseName := path.Base(remote) |
| 876 | origName := strings.TrimSuffix(origBaseName, path.Ext(origBaseName)) |
| 877 | origType := strings.TrimPrefix(path.Ext(origBaseName), ".") |
| 878 | |
| 879 | // Create directory if it doesn't exist |
| 880 | _, dirID, err := o.f.dirCache.FindPath(ctx, remote, true) |
| 881 | if err != nil { |
| 882 | return err |
| 883 | } |
| 884 | |
| 885 | // rename based rollback pattern |
| 886 | // old file is preserved until new upload succeeds |
| 887 | |
| 888 | var backupUUID string |
| 889 | var backupName, backupType string |
| 890 | oldUUID := o.uuid |
| 891 | |
| 892 | // Step 1: If file exists, rename to backup (preserves old file during upload) |
| 893 | if oldUUID != "" { |
| 894 | // Generate unique backup name |
| 895 | baseName := path.Base(remote) |
| 896 | name := strings.TrimSuffix(baseName, path.Ext(baseName)) |
| 897 | ext := strings.TrimPrefix(path.Ext(baseName), ".") |
| 898 | |
| 899 | backupSuffix := fmt.Sprintf(".rclone-backup-%s", random.String(8)) |
| 900 | backupName = o.f.opt.Encoding.FromStandardName(name + backupSuffix) |
| 901 | backupType = ext |
| 902 | |
| 903 | // Rename existing file to backup name |
| 904 | err = o.f.pacer.Call(func() (bool, error) { |
| 905 | err := files.RenameFile(ctx, o.f.cfg, oldUUID, backupName, backupType) |
| 906 | if err != nil { |
| 907 | // Handle 409 Conflict: Treat as success. |
| 908 | var httpErr *sdkerrors.HTTPError |
| 909 | if errors.As(err, &httpErr) && httpErr.StatusCode() == 409 { |
| 910 | return false, nil |
| 911 | } |
| 912 | } |
| 913 | return o.f.shouldRetry(ctx, err) |
| 914 | }) |
| 915 | if err != nil { |
| 916 | return fmt.Errorf("failed to rename existing file to backup: %w", err) |
| 917 | } |
| 918 | backupUUID = oldUUID |
| 919 | |
| 920 | fs.Debugf(o.f, "Renamed existing file %s to backup %s.%s (UUID: %s)", remote, backupName, backupType, backupUUID) |
| 921 | } |
| 922 | |
| 923 | size := src.Size() |
| 924 | |
| 925 | var meta *buckets.CreateMetaResponse |
| 926 | if size < 0 || size >= int64(o.f.opt.UploadCutoff) { |
| 927 | chunkWriter, uploadErr := multipart.UploadMultipart(ctx, src, in, multipart.UploadMultipartOptions{ |
| 928 | Open: o.f, |
| 929 | OpenOptions: options, |
no test coverage detected