Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close file on invalid range #15166

Merged
merged 5 commits into from
Apr 3, 2021
Merged

Close file on invalid range #15166

merged 5 commits into from
Apr 3, 2021

Conversation

KN4CK3R
Copy link
Member

@KN4CK3R KN4CK3R commented Mar 26, 2021

@KN4CK3R KN4CK3R mentioned this pull request Mar 26, 2021
11 tasks
Copy link
Member

@noerw noerw left a comment

Choose a reason for hiding this comment

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

Not very familiar with this code, but shouldn't we close in the other error-cases too? (Unable to seek)

@@ -53,6 +53,10 @@ func (s *ContentStore) Get(meta *models.LFSMetaObject, fromByte int64) (io.ReadC
}
if fromByte > 0 {
if fromByte >= meta.Size {
err = f.Close()
if err != nil {
log.Error("Whilst trying to read LFS OID[%s]: Unable to close Error: %v", meta.Oid, err)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
log.Error("Whilst trying to read LFS OID[%s]: Unable to close Error: %v", meta.Oid, err)
log.Error("Whilst trying to read LFS OID[%s]: Unable to close: %v", meta.Oid, err)

Copy link
Contributor

Choose a reason for hiding this comment

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

Not very familiar with this code, but shouldn't we close in the other error-cases too? (Unable to seek)

Yup it's gonna need that too

Copy link
Contributor

Choose a reason for hiding this comment

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

The rest of log messages in that function look like this - so if you want to clean this up - you need to clean them all up.

@GiteaBot GiteaBot added lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Mar 26, 2021
@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Mar 26, 2021
Signed-off-by: Andrew Thornton <art27@cantab.net>
return nil, ErrRangeNotSatisfiable{
FromByte: fromByte,
}
}
_, err = f.Seek(fromByte, io.SeekStart)
if err != nil {
log.Error("Whilst trying to read LFS OID[%s]: Unable to seek to %d Error: %v", meta.Oid, fromByte, err)
errClose := f.Close()
Copy link
Member

Choose a reason for hiding this comment

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

If we close the f here, but we don't return immediately. but we still return f out of the function.

@KN4CK3R
Copy link
Member Author

KN4CK3R commented Mar 26, 2021

I don't think all should be canceled if Seek fails. How about remove the Seek part from this method? It is only used by the LFS server to resume downloads. Other calls just pass 0 as fromByte. The Seek should happen in the LFS server code which can handle it better (just present the full file to download).

@lunny
Copy link
Member

lunny commented Mar 26, 2021

If don't cancel when seek failed, it may read wrong bytes.

@KN4CK3R
Copy link
Member Author

KN4CK3R commented Mar 26, 2021

What do you think? For me it looks cleaner with fewer logging noise.

--- a/modules/lfs/content_store.go
+++ b/modules/lfs/content_store.go
@@ -45,32 +45,13 @@ type ContentStore struct {
 
 // Get takes a Meta object and retrieves the content from the store, returning it as an io.ReadSeekCloser.
-func (s *ContentStore) Get(meta *models.LFSMetaObject, fromByte int64) (io.ReadCloser, error) {
+func (s *ContentStore) Get(meta *models.LFSMetaObject) (io.ReadSeekCloser, error) {
 	f, err := s.Open(meta.RelativePath())
 	if err != nil {
 		log.Error("Whilst trying to read LFS OID[%s]: Unable to open Error: %v", meta.Oid, err)
 		return nil, err
 	}
-	if fromByte > 0 {
-		if fromByte >= meta.Size {
-			err = f.Close()
-			if err != nil {
-				log.Error("Whilst trying to read LFS OID[%s]: Unable to close Error: %v", meta.Oid, err)
-			}
-			return nil, ErrRangeNotSatisfiable{
-				FromByte: fromByte,
-			}
-		}
-		_, err = f.Seek(fromByte, io.SeekStart)
-		if err != nil {
-			log.Error("Whilst trying to read LFS OID[%s]: Unable to seek to %d Error: %v", meta.Oid, fromByte, err)
-			errClose := f.Close()
-			if errClose != nil {
-				log.Error("Whilst trying to read LFS OID[%s]: Unable to close Error: %v", meta.Oid, errClose)
-			}
-		}
-	}
-	return f, err
+	return f, nil
 }
 
--- a/modules/lfs/pointers.go
+++ b/modules/lfs/pointers.go
@@ -67,5 +67,5 @@ func IsPointerFile(buf *[]byte) *models.LFSMetaObject {
 // ReadMetaObject will read a models.LFSMetaObject and return a reader
 func ReadMetaObject(meta *models.LFSMetaObject) (io.ReadCloser, error) {
 	contentStore := &ContentStore{ObjectStorage: storage.LFS}
-	return contentStore.Get(meta, 0)
+	return contentStore.Get(meta)
 }

--- a/modules/lfs/server.go
+++ b/modules/lfs/server.go
@@ -175,6 +175,11 @@ func getContentHandler(ctx *context.Context) {
 			statusCode = 206
 			fromByte, _ = strconv.ParseInt(match[1], 10, 32)
 
+			if fromByte >= meta.Size {
+				writeStatus(ctx, http.StatusRequestedRangeNotSatisfiable)
+				return
+			}
+
 			if match[2] != "" {
				_toByte, _ := strconv.ParseInt(match[2], 10, 32)
				if _toByte >= fromByte && _toByte < toByte {
					toByte = _toByte
				}
			}

			ctx.Resp.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", fromByte, toByte, meta.Size-fromByte))
			ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Range")
		}
	}
 
 	contentStore := &ContentStore{ObjectStorage: storage.LFS}
-	content, err := contentStore.Get(meta, fromByte)
+	content, err := contentStore.Get(meta)
 	if err != nil {
-		if IsErrRangeNotSatisfiable(err) {
-			writeStatus(ctx, http.StatusRequestedRangeNotSatisfiable)
-		} else {
-			// Errors are logged in contentStore.Get
-			writeStatus(ctx, 404)
-		}
+		// Errors are logged in contentStore.Get
+		writeStatus(ctx, http.StatusNotFound)
 		return
 	}
 	defer content.Close()
 
+	if fromByte > 0 {
+		_, err = content.Seek(fromByte, io.SeekStart)
+		if err != nil {
+			log.Error("Whilst trying to read LFS OID[%s]: Unable to seek to %d Error: %v", meta.Oid, fromByte, err)
+
+			writeStatus(ctx, http.StatusInternalServerError)
+			return
+		}
+	}
+
 	contentLength := toByte + 1 - fromByte
 	ctx.Resp.Header().Set("Content-Length", strconv.FormatInt(contentLength, 10))
 	ctx.Resp.Header().Set("Content-Type", "application/octet-stream")

@lunny
Copy link
Member

lunny commented Mar 31, 2021

@KN4CK3R Further more, we can remove Get method, because we already have Open and Get is the same as Open after removed the frombyte parameter.

@KN4CK3R
Copy link
Member Author

KN4CK3R commented Mar 31, 2021

I thought about that too but kept the Get method to hide the meta.RelativePath() internal detail. One step further would be a private inner storage to remove the public Open method.

type ContentStore struct {
	storage storage.ObjectStorage
}

@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Apr 1, 2021
@6543
Copy link
Member

6543 commented Apr 3, 2021

I guess it's a bugfix...?

backport to v1.14?

@6543 6543 added this to the 1.15.0 milestone Apr 3, 2021
@lunny lunny added the type/bug label Apr 3, 2021
@lunny
Copy link
Member

lunny commented Apr 3, 2021

I guess it's a bugfix...?

backport to v1.14?

please

@6543
Copy link
Member

6543 commented Apr 3, 2021

🚀

@6543 6543 merged commit 3cc7d27 into go-gitea:master Apr 3, 2021
@6543
Copy link
Member

6543 commented Apr 3, 2021

@KN4CK3R please backport 🚀

KN4CK3R added a commit to KN4CK3R/gitea that referenced this pull request Apr 3, 2021
* Close file on invalid range.

* Close on seek error

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
@KN4CK3R
Copy link
Member Author

KN4CK3R commented Apr 3, 2021

While creating the backport I noticed I forgot to push the proposed changes from #15166 (comment) The current code is not really good because of the potential double Close. I will create a second PR with the changes.

@techknowlogick techknowlogick added the backport/done All backports for this PR have been created label Apr 3, 2021
lunny added a commit that referenced this pull request Apr 6, 2021
* Close file on invalid range.

* Close on seek error

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Moved 'Seek' into server.

* io.ReadSeekCloser is only available in Go 1.16

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
KN4CK3R added a commit to KN4CK3R/gitea that referenced this pull request Apr 6, 2021
)

* Close file on invalid range.

* Close on seek error

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Moved 'Seek' into server.

* io.ReadSeekCloser is only available in Go 1.16

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
techknowlogick added a commit that referenced this pull request Apr 6, 2021
* Close file on invalid range.

* Close on seek error

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Moved 'Seek' into server.

* io.ReadSeekCloser is only available in Go 1.16

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
@KN4CK3R KN4CK3R deleted the fix-close-file branch April 6, 2021 20:27
@go-gitea go-gitea locked and limited conversation to collaborators May 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
backport/done All backports for this PR have been created lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. type/bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants