ronantakizawa/codereview-bench huggingface dataset:
Repository: rclone/rclone
File: backend/protondrive/protondrive.go (Go)

Code before:
			Required:   true,
			IsPassword: true,
		}, {
			Name: "mailbox_password",
			Help: `The mailbox password of your two-password proton account.

For more information regarding the mailbox password, please check the 
following official knowledge base article: 
https://proton.me/support/the-difference-between-the-mailbox-password-and-login-password
`,
			IsPassword: true,
			Advanced:   true,
		}, {
			Name: "2fa",
			Help: `The 2FA code

The value can also be provided with --protondrive-2fa=000000

The 2FA code of your proton drive account if the account is set up with 
two-factor authentication`,
			Required: false,
		}, {
			Name: "otp_secret_key",
			Help: `The OTP secret key

The value can also be provided with --protondrive-otp_secret_key=00000

The OTP secret key of your proton drive account if the account is set up with 
two-factor authentication`,
			Required:   false,
			Sensitive:  true,
			IsPassword: true,
		}, {
			Name:      clientUIDKey,
			Help:      "Client uid key (internal use only)",
			Required:  false,
			Advanced:  true,
			Sensitive: true,
			Hide:      fs.OptionHideBoth,
		}, {
			Name:      clientAccessTokenKey,
			Help:      "Client access token key (internal use only)",
			Required:  false,
			Advanced:  true,
			Sensitive: true,
			Hide:      fs.OptionHideBoth,
		}, {
			Name:      clientRefreshTokenKey,
			Help:      "Client refresh token key (internal use only)",
			Required:  false,
			Advanced:  true,

Review comment (security):
The CLI flag will be `otp-secret-key` with the `_` replaced by `-`.

Code after:
			Required:   true,
			IsPassword: true,
		}, {
			Name: "mailbox_password",
			Help: `The mailbox password of your two-password proton account.

For more information regarding the mailbox password, please check the 
following official knowledge base article: 
https://proton.me/support/the-difference-between-the-mailbox-password-and-login-password
`,
			IsPassword: true,
			Advanced:   true,
		}, {
			Name: "2fa",
			Help: `The 2FA code

The value can also be provided with --protondrive-2fa=000000

The 2FA code of your proton drive account if the account is set up with 
two-factor authentication`,
			Required: false,
		}, {
			Name: "otp_secret_key",
			Help: `The OTP secret key

The value can also be provided with --protondrive-otp-secret-key=ABCDEFGHIJKLMNOPQRSTUVWXYZ234567

The OTP secret key of your proton drive account if the account is set up with 
two-factor authentication`,
			Required:   false,
			Sensitive:  true,
			IsPassword: true,
		}, {
			Name:      clientUIDKey,
			Help:      "Client uid key (internal use only)",
			Required:  false,
			Advanced:  true,
			Sensitive: true,
			Hide:      fs.OptionHideBoth,
		}, {
			Name:      clientAccessTokenKey,
			Help:      "Client access token key (internal use only)",
			Required:  false,
			Advanced:  true,
			Sensitive: true,
			Hide:      fs.OptionHideBoth,
		}, {
			Name:      clientRefreshTokenKey,
			Help:      "Client refresh token key (internal use only)",
			Required:  false,
			Advanced:  true,
Repository: rclone/rclone
File: backend/swift/swift_test.go (Go)

Code before:
		ModTime: fstest.Time("2020-12-31T04:05:06.499999999Z"),
		Path:    "large.txt",
		Size:    -1, // use unknown size during upload
	}
	const contentSize = 2048
	contents := random.String(contentSize)
	buf := bytes.NewBufferString(contents)
	uploadHash := hash.NewMultiHasher()
	in := io.TeeReader(buf, uploadHash)

	file.Size = -1
	obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
	ctx := context.TODO()
	obj, err := f.Features().PutStream(ctx, in, obji)
	require.NoError(t, err)
	require.NotEmpty(t, obj)
	remoteTarget := "large.txt (copy)"
	objTarget, err := f.Features().Copy(ctx, obj, remoteTarget)
	require.NoError(t, err)
	require.NotEmpty(t, objTarget)
	require.Equal(t, obj.Size(), objTarget.Size())
}

func (f *Fs) testPolicyDiscovery(t *testing.T) {
	// Default policy for SwiftAIO is Policy-0.
	assert.Equal(t, "Policy-0", f.opt.StoragePolicy)
}

var _ fstests.InternalTester = (*Fs)(nil)


Review comment (bug):
This test, as written, relies on the other tests being run first. This doesn't always happen especially in the integration tester. So I'd factor the setting out and call it explicitly (see above).

This could fail in the integration tests if we had any swift storage backends that weren't SAIO but we don't currently!

Code after:
		ModTime: fstest.Time("2020-12-31T04:05:06.499999999Z"),
		Path:    "large.txt",
		Size:    -1, // use unknown size during upload
	}
	const contentSize = 2048
	contents := random.String(contentSize)
	buf := bytes.NewBufferString(contents)
	uploadHash := hash.NewMultiHasher()
	in := io.TeeReader(buf, uploadHash)

	file.Size = -1
	obji := object.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil)
	ctx := context.TODO()
	obj, err := f.Features().PutStream(ctx, in, obji)
	require.NoError(t, err)
	require.NotEmpty(t, obj)
	remoteTarget := "large.txt (copy)"
	objTarget, err := f.Features().Copy(ctx, obj, remoteTarget)
	require.NoError(t, err)
	require.NotEmpty(t, objTarget)
	require.Equal(t, obj.Size(), objTarget.Size())
}

func (f *Fs) testPolicyDiscovery(t *testing.T) {
	ctx := context.TODO()
	container := "testPolicyDiscovery-1"
	// Reset the policy so we can test if it is populated.
	f.opt.StoragePolicy = ""
	err := f.makeContainer(ctx, container)
	require.NoError(t, err)
	_, err = f.fetchStoragePolicy(ctx, container)
	require.NoError(t, err)

	// Default policy for SAIO image is 1replica.
	assert.Equal(t, "1replica", f.opt.StoragePolicy)

	// Create a container using a non-default policy, and check to ensure
	// that the created segments container uses the same non-default policy.
	policy := "Policy-1"
	container = "testPolicyDiscovery-2"

	f.opt.StoragePolicy = policy
	err = f.makeContainer(ctx, container)
	require.NoError(t, err)

	// Reset the policy so we can test if it is populated, and set to the
	// non-default policy.
	f.opt.StoragePolicy = ""
	_, err = f.fetchStoragePolicy(ctx, container)
	require.NoError(t, err)
	assert.Equal(t, policy, f.opt.StoragePolicy)
Repository: rclone/rclone
File: backend/b2/b2.go (Go)

Code before:
			bucket := entry.Remote()
			err = listR(bucket, "", f.rootDirectory, true)
			if err != nil {
				return err
			}
			// bucket must be present if listing succeeded
			f.cache.MarkOK(bucket)
		}
	} else {
		err = listR(bucket, directory, f.rootDirectory, f.rootBucket == "")
		if err != nil {
			return err
		}
		// bucket must be present if listing succeeded
		f.cache.MarkOK(bucket)
	}
	return list.Flush()
}

// listBucketFn is called from listBucketsToFn to handle a bucket
type listBucketFn func(*api.Bucket) error

// listBucketsToFn lists the buckets to the function supplied
func (f *Fs) listBucketsToFn(ctx context.Context, bucketName string, fn listBucketFn) error {
	var bucketID string
	for _, b := range f.info.APIs.Storage.Allowed.Buckets {
		if b.Name == bucketName {
			bucketID = b.ID
			break
		}
	}
	var account = api.ListBucketsRequest{
		AccountID: f.info.AccountID,
		BucketID:  bucketID,
	}
	if bucketName != "" && account.BucketID == "" {
		account.BucketName = f.opt.Enc.FromStandardName(bucketName)
	}

	var response api.ListBucketsResponse
	opts := rest.Opts{
		Method: "POST",
		Path:   "/b2_list_buckets",
	}
	err := f.pacer.Call(func() (bool, error) {
		resp, err := f.srv.CallJSON(ctx, &opts, &account, &response)
		return f.shouldRetry(ctx, resp, err)
	})
	if err != nil {
		return err
	}

Review comment (suggestion):
I'm not sure this is 100% correct. If this is called with `bucketName == ""` then it should list all the buckets we are allowed to look at and I don't think it is doing that.

Otherwise this function is used for looking up bucket IDs

Code after:
			}
			bucket := entry.Remote()
			err = listR(bucket, "", f.rootDirectory, true)
			if err != nil {
				return err
			}
			// bucket must be present if listing succeeded
			f.cache.MarkOK(bucket)
		}
	} else {
		err = listR(bucket, directory, f.rootDirectory, f.rootBucket == "")
		if err != nil {
			return err
		}
		// bucket must be present if listing succeeded
		f.cache.MarkOK(bucket)
	}
	return list.Flush()
}

// listBucketFn is called from listBucketsToFn to handle a bucket
type listBucketFn func(*api.Bucket) error

// listBucketsToFn lists the buckets to the function supplied
func (f *Fs) listBucketsToFn(ctx context.Context, bucketName string, fn listBucketFn) error {
	responses := make([]api.ListBucketsResponse, len(f.info.APIs.Storage.Allowed.Buckets))[:0]

	for i := range f.info.APIs.Storage.Allowed.Buckets {
		b := &f.info.APIs.Storage.Allowed.Buckets[i]
		// Empty names indicate a bucket that no longer exists, this is non-fatal
		// for multi-bucket API keys.
		if b.Name == "" {
			continue
		}
		// When requesting a specific bucket skip over non-matching names
		if bucketName != "" && b.Name != bucketName {
			continue
		}

		var account = api.ListBucketsRequest{
			AccountID: f.info.AccountID,
			BucketID:  b.ID,
		}
		if bucketName != "" && account.BucketID == "" {
			account.BucketName = f.opt.Enc.FromStandardName(bucketName)
		}

		var response api.ListBucketsResponse
		opts := rest.Opts{
			Method: "POST",
			Path:   "/b2_list_buckets",
Repository: rclone/rclone
File: backend/pcloud/pcloud.go (Go)

Code before:
		case <-ctx.Done():
			return
		case newInterval, ok := <-ch:
			if !ok { return }
			interval = newInterval
		default:
		}

		// 2. Setup /diff Request
		opts := rest.Opts{
			Method:     "GET",
			Path:       "/diff",
			Parameters: url.Values{},
		}

		if diffID != 0 {
			opts.Parameters.Set("diffid", strconv.FormatInt(diffID, 10))
			opts.Parameters.Set("block", "1")
		} else {
			opts.Parameters.Set("last", "0") 
		}

		// 3. Perform Long-Poll
		// Timeout set to 90s (server usually blocks for 60s max)
		reqCtx, cancel := context.WithTimeout(ctx, 90*time.Second)
		var result struct {
			Result  int              `json:"result"`
			DiffID  int64            `json:"diffid"`
			Entries []map[string]any `json:"entries"`
			Error   string           `json:"error"`
		}
		
		_, err := f.srv.CallJSON(reqCtx, &opts, nil, &result)
		cancel()

		if err != nil {
			if errors.Is(err, context.Canceled) { return }
			// Ignore timeout errors as they are normal for long-polling
			if !errors.Is(err, context.DeadlineExceeded) {
				fs.Infof(f, "ChangeNotify: polling error: %v. Waiting %v.", err, interval)
				time.Sleep(interval)
			}
			continue
		}

		// If result is not 0, reset DiffID to resync
		if result.Result != 0 {
			diffID = 0
			time.Sleep(2 * time.Second)
			continue
		}

Review comment (suggestion):
Define the API structures in `api/types.go` not here

Code after:

// Remote returns the remote path
func (o *Object) Remote() string {
	return o.remote
}

// getHashes fetches the hashes into the object
func (o *Object) getHashes(ctx context.Context) (err error) {
	var resp *http.Response
	var result api.ChecksumFileResult
	opts := rest.Opts{
		Method:     "GET",
		Path:       "/checksumfile",
		Parameters: url.Values{},
	}
	opts.Parameters.Set("fileid", fileIDtoNumber(o.id))
	err = o.fs.pacer.Call(func() (bool, error) {
		resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &result)
		err = result.Error.Update(err)
		return shouldRetry(ctx, resp, err)
	})
	if err != nil {
		return err
	}
	o.setHashes(&result.Hashes)
	return o.setMetaData(&result.Metadata)
}

// Hash returns the SHA-1 of an object returning a lowercase hex string
func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
	var pHash *string
	switch t {
	case hash.MD5:
		pHash = &o.md5
	case hash.SHA1:
		pHash = &o.sha1
	case hash.SHA256:
		pHash = &o.sha256
	default:
		return "", hash.ErrUnsupported
	}
	if o.md5 == "" && o.sha1 == "" && o.sha256 == "" {
		err := o.getHashes(ctx)
		if err != nil {
			return "", fmt.Errorf("failed to get hash: %w", err)
		}
	}
	return *pHash, nil
}

// Size returns the size of an object in bytes
Repository: rclone/rclone
File: backend/filelu/filelu.go (Go)

Code before:

		if err := src.Remove(ctx); err != nil {
			return nil, fmt.Errorf("failed to remove source file: %w", err)
		}

		return nil, nil
	}

	size := src.Size()

	reader, err := src.Open(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to open source object: %w", err)
	}
	defer func() {
		if err := reader.Close(); err != nil {
			fs.Logf(nil, "Failed to close file body: %v", err)
		}
	}()

	fullPath := destinationPath
	if !strings.HasPrefix(fullPath, "/") {
		fullPath = path.Join(f.root, fullPath)
	}

	if size <= 500*1024*1024 {
		err = f.uploadFile(ctx, reader, destinationPath)
		if err != nil {
			return nil, fmt.Errorf("failed to upload small file: %w", err)
		}
	} else {
		err = f.multipartUpload(ctx, reader, fullPath)
		if err != nil {
			return nil, fmt.Errorf("failed to multipart upload: %w", err)
		}
	}
	if err := src.Remove(ctx); err != nil {
		return nil, fmt.Errorf("failed to delete source file: %w", err)
	}

	return &Object{
		fs:      f,
		remote:  destinationPath,
		size:    src.Size(),
		modTime: src.ModTime(ctx),
	}, nil
}

// Rmdir removes a directory
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
	fullPath := path.Join(f.root, dir)

Review comment (suggestion):
Move is supposed to be a **server side** move so you shouldn't be downloading and uploading here.

Remove the `Move` method entirely if you can't do a server side move.

Code after:

		if err := src.Remove(ctx); err != nil {
			return nil, fmt.Errorf("failed to remove source file: %w", err)
		}

		return nil, nil
	}

	reader, err := src.Open(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to open source object: %w", err)
	}
	defer func() {
		if err := reader.Close(); err != nil {
			fs.Logf(nil, "Failed to close file body: %v", err)
		}
	}()

	err = f.uploadFile(ctx, reader, destinationPath)
	if err != nil {
		return nil, fmt.Errorf("failed to upload file to destination: %w", err)
	}

	if err := src.Remove(ctx); err != nil {
		return nil, fmt.Errorf("failed to delete source file: %w", err)
	}

	return &Object{
		fs:      f,
		remote:  destinationPath,
		size:    src.Size(),
		modTime: src.ModTime(ctx),
	}, nil
}

// Rmdir removes a directory
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
	fullPath := path.Join(f.root, dir)
	if fullPath != "" {
		fullPath = "/" + strings.Trim(fullPath, "/")
	}

	// Step 1: Check if folder is empty
	listResp, err := f.getFolderList(ctx, fullPath)
	if err != nil {
		return err
	}
	if len(listResp.Result.Files) > 0 || len(listResp.Result.Folders) > 0 {
		return fmt.Errorf("Rmdir: directory %q is not empty", fullPath)
	}
Repository: rclone/rclone
File: backend/filelu/filelu.go (Go)

Code before:
		remotePathWithoutRoot = strings.TrimPrefix(remotePathWithoutRoot, "/")
		entries = append(entries, fs.NewDir(remotePathWithoutRoot, time.Now()))
	}
	for _, file := range result.Result.Files {
		if _, ok := fldMap[file.FldID.String()]; ok {
			continue
		}
		remote := path.Join(dir, file.Name)
		// trim leading slashes
		remote = strings.TrimPrefix(remote, "/")
		obj := &Object{
			fs:      f,
			remote:  remote,
			size:    file.Size,
			modTime: time.Now(),
		}
		entries = append(entries, obj)
	}
	return entries, nil
}

// Put uploads a file directly to the destination folder in the FileLu storage system.
func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
	size := src.Size()

	if size <= 500*1024*1024 {
		err := f.uploadFile(ctx, in, src.Remote())
		if err != nil {
			return nil, err
		}
	} else {
		fullPath := path.Join(f.root, src.Remote())
		err := f.multipartUpload(ctx, in, fullPath)
		if err != nil {
			return nil, err
		}
	}

	newObject := &Object{
		fs:      f,
		remote:  src.Remote(),
		size:    src.Size(),
		modTime: src.ModTime(ctx),
	}
	return newObject, nil
}

// Move moves the file to the specified location
func (f *Fs) Move(ctx context.Context, src fs.Object, destinationPath string) (fs.Object, error) {

	if strings.HasPrefix(destinationPath, "/") || strings.Contains(destinationPath, ":\\") {

Review comment (suggestion):
This constant is called `upload_cutoff` in other backends. Please make a config option for it, eg

https://github.com/rclone/rclone/blob/341ce61a2aa535b5c555ade033dfa82b0fab4474/backend/s3/s3.go#L225-L233

Code after:
		}

		paths := strings.Split(folder.Path, fullPath+"/")
		remote := paths[0]
		if len(paths) > 1 {
			remote = paths[1]
		}

		if strings.Contains(remote, "/") {
			continue
		}

		pathsWithoutRoot := strings.Split(folder.Path, "/"+f.root+"/")
		remotePathWithoutRoot := pathsWithoutRoot[0]
		if len(pathsWithoutRoot) > 1 {
			remotePathWithoutRoot = pathsWithoutRoot[1]
		}
		remotePathWithoutRoot = strings.TrimPrefix(remotePathWithoutRoot, "/")
		entries = append(entries, fs.NewDir(remotePathWithoutRoot, time.Now()))
	}
	for _, file := range result.Result.Files {
		if _, ok := fldMap[file.FldID.String()]; ok {
			continue
		}
		remote := path.Join(dir, file.Name)
		// trim leading slashes
		remote = strings.TrimPrefix(remote, "/")
		obj := &Object{
			fs:      f,
			remote:  remote,
			size:    file.Size,
			modTime: time.Now(),
		}
		entries = append(entries, obj)
	}
	return entries, nil
}

// Put uploads a file directly to the destination folder in the FileLu storage system.
func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
	o := &Object{
		fs:     f,
		remote: src.Remote(),
	}
	return o, o.Update(ctx, in, src, options...)
}

// Move moves the file to the specified location
func (f *Fs) Move(ctx context.Context, src fs.Object, destinationPath string) (fs.Object, error) {

	if strings.HasPrefix(destinationPath, "/") || strings.Contains(destinationPath, ":\\") {
Repository: rclone/rclone
File: backend/filelu/filelu_client.go (Go)

Code before:
// MultipartInitResponse represents the response from multipart/init.
type MultipartInitResponse struct {
	Status int    `json:"status"`
	Msg    string `json:"msg"`
	Result struct {
		UploadID   string `json:"upload_id"`
		SessID     string `json:"sess_id"`
		Server     string `json:"server"`
		FolderID   int64  `json:"folder_id"`
		ObjectPath string `json:"object_path"`
	} `json:"result"`
}

// multipartInit starts a new multipart upload and returns server details.
func (f *Fs) multipartInit(ctx context.Context, folderPath, filename string) (*MultipartInitResponse, error) {
	apiURL := fmt.Sprintf("%s/multipart/init?key=%s&filename=%s&folder_path=%s",
		f.endpoint,
		url.QueryEscape(f.opt.Key),
		url.QueryEscape(filename),
		url.QueryEscape(folderPath),
	)

	var result MultipartInitResponse

	err := f.pacer.Call(func() (bool, error) {
		req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
		if err != nil {
			return false, err
		}

		resp, err := f.client.Do(req)
		if err != nil {
			return false, err
		}
		defer func() { _ = resp.Body.Close() }()

		if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
			return false, err
		}

		if result.Status != 200 {
			return false, fmt.Errorf("multipart init error: %s", result.Msg)
		}

		return false, err
	})

	if err != nil {
		return nil, err
	}


Review comment (suggestion):
Not sure why you aren't using `lib/rest` here?

In fact you don't appear to be using it anywhere in the backend :-(

Code after:
// multipartInit starts a new multipart upload and returns server details.
func (f *Fs) multipartInit(ctx context.Context, folderPath, filename string) (*api.MultipartInitResponse, error) {
	opts := rest.Opts{
		Method: "GET",
		Path:   "/multipart/init",
		Parameters: url.Values{
			"key":         {f.opt.Key},
			"filename":    {filename},
			"folder_path": {folderPath},
		},
	}

	var result api.MultipartInitResponse

	err := f.pacer.Call(func() (bool, error) {
		_, err := f.srv.CallJSON(ctx, &opts, nil, &result)
		return fserrors.ShouldRetry(err), err
	})
	if err != nil {
		return nil, err
	}

	if result.Status != 200 {
		return nil, fmt.Errorf("multipart init error: %s", result.Msg)
	}

	return &result, nil
}

// completeMultipart finalizes the multipart upload on the file server.
func (f *Fs) completeMultipart(ctx context.Context, server string, uploadID string, sessID string, objectPath string) error {
	req, err := http.NewRequestWithContext(ctx, "POST", server, nil)
	if err != nil {
		return err
	}

	req.Header.Set("X-RC-Upload-Id", uploadID)
	req.Header.Set("X-Sess-ID", sessID)
	req.Header.Set("X-Object-Path", objectPath)

	resp, err := f.client.Do(req)
	if err != nil {
		return err
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != 202 {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("completeMultipart failed %d: %s", resp.StatusCode, string(body))
	}
	return nil
Repository: rclone/rclone
File: backend/filelu/filelu_object.go (Go)

Code before:
			}
		case *fs.SeekOption:
			offset = x.Offset
			count = o.size
		default:
			if option.Mandatory() {
				fs.Logf(o, "Unsupported mandatory option: %v", option)
			}
		}
	}

	var reader io.ReadCloser

	err = o.fs.pacer.Call(func() (bool, error) {
		req, err := http.NewRequestWithContext(ctx, "GET", directLink, nil)
		if err != nil {
			return false, err
		}

		resp, err := o.fs.client.Do(req)
		if err != nil {
			return shouldRetry(err), err
		}

		if resp.StatusCode != http.StatusOK {
			body, _ := io.ReadAll(resp.Body)
			_ = resp.Body.Close()
			return false, fmt.Errorf("failed to download file: HTTP %d %s", resp.StatusCode, body)
		}

		if offset > 0 {
			_, err = io.CopyN(io.Discard, resp.Body, offset)
			if err != nil {
				_ = resp.Body.Close()
				return false, fmt.Errorf("failed to skip offset: %w", err)
			}
		}

		if count > 0 {
			reader = struct {
				io.Reader
				io.Closer
			}{
				Reader: io.LimitReader(resp.Body, count),
				Closer: resp.Body,
			}
		} else {
			reader = resp.Body
		}

		return false, nil

Review comment (suggestion):
This change should be in a separate commit.

Also can you remove the spurious changes from this file and put back the error wrapping and comments.

I assume you are using AI to help you code - that is fine - but please keep control of what it changes and doesn't change.

Code after:
			}
		case *fs.SeekOption:
			offset = x.Offset
			count = o.size
		default:
			if option.Mandatory() {
				fs.Logf(o, "Unsupported mandatory option: %v", option)
			}
		}
	}

	// Wrap the response body to handle offset and count
	var reader io.ReadCloser
	err = o.fs.pacer.Call(func() (bool, error) {
		req, err := http.NewRequestWithContext(ctx, "GET", directLink, nil)
		if err != nil {
			return false, fmt.Errorf("failed to create download request: %w", err)
		}

		resp, err := o.fs.client.Do(req)
		if err != nil {
			return shouldRetry(err), fmt.Errorf("failed to download file: %w", err)
		}

		if resp.StatusCode != http.StatusOK {
			defer func() {
				if err := resp.Body.Close(); err != nil {
					fs.Logf(nil, "Failed to close response body: %v", err)
				}
			}()
			return false, fmt.Errorf("failed to download file: HTTP %d", resp.StatusCode)
		}

		if offset > 0 {
			_, err = io.CopyN(io.Discard, resp.Body, offset)
			if err != nil {
				_ = resp.Body.Close()
				return false, fmt.Errorf("failed to skip offset: %w", err)
			}
		}

		if count > 0 {
			reader = struct {
				io.Reader
				io.Closer
			}{
				Reader: io.LimitReader(resp.Body, count),
				Closer: resp.Body,
			}
		} else {
			reader = resp.Body