12a13 > "net/url" 81c82,99 < isPull = ctx.Req.Method == "GET" --- > // In addition to GET requests, HEAD requests are also "pull" > // operations (reads), so they should also not require > // authentication. This is necessary for git-annex to operate > // properly, as it emits HEAD requests to check for the > // existence of keys, e.g. before dropping locally, and asking > // for authentication would break unauthenticated http usage in > // this situation. > // It should be safe to make all HEAD requests require no > // authentication, but as it is only necessary for the > // annex/objects endpoints to fix git-annex' drop operations it > // is limited to those for now. > r, err := regexp.Compile("^/?" + username + "/" + reponame + "(.git)?/annex/objects") > if err != nil { > ctx.ServerError("failed to create URL path regex", err) > return nil > } > isPull = ctx.Req.Method == "GET" || > r.MatchString(ctx.Req.URL.Path) && ctx.Req.Method == "HEAD" 470c488 < if !git.IsErrCanceledOrKilled(err) { --- > if err.Error() != "signal: killed" { 547a566,601 > // GetConfig implements fetching the git config of a repository > func GetConfig(ctx *context.Context) { > h := httpBase(ctx) > if h != nil { > setHeaderNoCache(ctx) > config, err := os.ReadFile(filepath.Join(h.getRepoDir(), "config")) > if err != nil { > log.Error("Failed to read git config file: %v", err) > ctx.Resp.WriteHeader(http.StatusInternalServerError) > return > } > if !setting.Annex.DisableP2PHTTP { > appURL, err := url.Parse(setting.AppURL) > if err != nil { > log.Error("Could not parse 'setting.AppURL': %v", err) > ctx.Resp.WriteHeader(http.StatusInternalServerError) > return > } > if appURL.Port() == "" { > // If there is no port set then set the http(s) default ports. > // Without this, git-annex would try its own default port (9417) and fail. > if appURL.Scheme == "http" { > appURL.Host += ":80" > } > if appURL.Scheme == "https" { > appURL.Host += ":443" > } > } > config = append(config, []byte("[annex]\n\turl = annex+"+appURL.String()+"git-annex-p2phttp\n")...) > } > ctx.Resp.Header().Set("Content-Type", "text/plain") > ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(config))) > http.ServeContent(ctx.Resp, ctx.Req, "config", time.Now(), bytes.NewReader(config)) > } > } > 597a652,682 > } > } > > // GetAnnexObject implements git-annex dumb HTTP > func GetAnnexObject(ctx *context.Context) { > h := httpBase(ctx) > if h != nil { > // git-annex objects are stored in .git/annex/objects/{hash1}/{hash2}/{key}/{key} > // where key is a string containing the size and (usually SHA256) checksum of the file, > // and hash1+hash2 are the first few bits of the md5sum of key itself. > // ({hash1}/{hash2}/ is just there to avoid putting too many files in one directory) > // ref: https://git-annex.branchable.com/internals/hashing/ > > // keyDir should = key, but we don't enforce that > object := filepath.Join(ctx.Params("hash1"), ctx.Params("hash2"), ctx.Params("keyDir"), ctx.Params("key")) > > // Sanitize the input against directory traversals. > // > // This works because at the filesystem root, "/.." = "/"; > // So if a path starts rooted ("/"), path.Clean(), which > // path.Join() calls internally, removes all '..' prefixes. > // After, this unroots the path unconditionally ([1:]), which > // works because we know the input is never supposed to be rooted. > // > // The router code probably also disallows "..", so this > // should be redundant, but it's defensive to keep it > // whenever touching filesystem paths with user input. > object = filepath.Join(string(filepath.Separator), object)[1:] > > setHeaderCacheForever(ctx) > h.sendFile(ctx, "application/octet-stream", "annex/objects/"+object)