Skip to content

Implement Copy, List, DeleteRecursive, Properties for DAV#103

Merged
kathap merged 10 commits into
mainfrom
dav-add-missing-ops
May 28, 2026
Merged

Implement Copy, List, DeleteRecursive, Properties for DAV#103
kathap merged 10 commits into
mainfrom
dav-add-missing-ops

Conversation

@kathap
Copy link
Copy Markdown
Contributor

@kathap kathap commented May 21, 2026

Replaces the stubs left from the two-layer refactor (#98). Adds PROPFIND and COPY support via WebDAV verbs and Basic Auth.

EnsureStorageExists is a no-op, matching the existing Ruby DavClient — WebDAV has no bucket concept, the blobstore VM provisions the root directory, and nginx auto-creates per-resource subdirs on first PUT.

Replaces the stubs left from the two-layer refactor (#98). Adds PROPFIND
and COPY support via WebDAV verbs and Basic Auth.

EnsureStorageExists is a no-op, matching the existing Ruby DavClient —
WebDAV has no bucket concept, the blobstore VM provisions the root
directory, and nginx auto-creates per-resource subdirs on first PUT.
Copy link
Copy Markdown
Contributor

@serdarozerr serdarozerr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good some small suggestions

Comment thread dav/client/storage_client.go Outdated
func (c *storageClient) Put(path string, content io.ReadCloser, contentLength int64) error {
defer content.Close() //nolint:errcheck

if err := validateBlobID(path); err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about calling validateBlogID in client.go, for example in this put we already open file and send content , if we already checked in here https://github.com/cloudfoundry/storage-cli/blob/main/dav/client/client.go#L54 as first condition then we could have skip all these file operations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea

Comment thread dav/client/storage_client.go Outdated
return fmt.Errorf("fetching properties of %q: status %d", blobPath, resp.StatusCode)
}

props := BlobProperties{ContentLength: resp.ContentLength}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this always returns some value, why not null check or something on this ContentLength

srcBlob, dstBlob, copyResp.StatusCode, c.readAndTruncateBody(copyResp))
}

func (c *storageClient) List(prefix string) ([]string, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why there is no validateBlobID for List command ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List and DeleteRecursive take a prefix, not a blob ID — they use validatePrefix in client.go

return hrefPath, nil
}

func (c *storageClient) DeleteRecursive(prefix string) error {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as in list function no validateBlobID ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List and DeleteRecursive take a prefix, not a blob ID — they use validatePrefix in client.go

Comment thread dav/client/helpers.go Outdated
Comment on lines +25 to +35
// validateBlobID rejects blob IDs that are unsafe to splice into a request
// path. The rules are intentionally strict: blob IDs come from external
// callers (e.g. CCNG, Diego) and a malformed value can confuse path joining,
// produce ambiguous URLs, or — worst case — escape the configured endpoint
// via path traversal. We refuse:
//
// - empty strings
// - leading or trailing slashes (the path joiner adds them itself)
// - empty path segments ("//")
// - "." or ".." segments (traversal)
// - control characters (CRLF / NUL injection into headers and URLs)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please shorten those AI comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will ask the AI to do so

Comment thread dav/client/helpers.go Outdated
Comment on lines +64 to +68
// validatePrefix is the more lenient sibling of validateBlobID, used for List.
// A directory-style prefix may legitimately end in "/" (e.g. "cc-droplets/"),
// but everything else still applies. An empty prefix is allowed at the caller
// level — List uses "" to mean "no filtering" — so this helper is only invoked
// when a non-empty prefix was supplied.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please shorten those AI comments

Comment thread dav/client/storage_client.go Outdated
return nil
}

// RFC 4918 §9.6.1: 207 Multi-Status means at least one child could not be
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know when this multi status is raised? Can this happen if a user wants to delete a whole directory but another user concurrently deletes a specific file in that that directory? Recursive delete should be idempotent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — since we switched DeleteRecursive to list-then-delete on individual files, 207 won't actually be
triggered there anymore. Removing it.

@github-project-automation github-project-automation Bot moved this from Inbox to Waiting for Changes | Open for Contribution in Foundational Infrastructure Working Group May 26, 2026
@github-project-automation github-project-automation Bot moved this from Waiting for Changes | Open for Contribution to Pending Merge | Prioritized in Foundational Infrastructure Working Group May 28, 2026
@kathap kathap merged commit f93f8a2 into main May 28, 2026
3 checks passed
@github-project-automation github-project-automation Bot moved this from Pending Merge | Prioritized to Done in Foundational Infrastructure Working Group May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

3 participants