From 229045a3aa13c76b9c1621e332df495aba0d34e0 Mon Sep 17 00:00:00 2001 From: Matyas Selmeci Date: Tue, 3 Dec 2024 23:11:18 +0000 Subject: [PATCH] xrootd: update to 5.7.2 (merged from 24-main r28135) git-svn-id: https://vdt.cs.wisc.edu/svn/native/redhat/branches/23-main@28136 4e558342-562e-0410-864c-e07659590f8c --- xrootd/osg/2300-stat-call-reduction.patch | 1114 ----------------- xrootd/osg/2348-cache-age-logic.patch | 75 +- .../osg/2357-fix-errSocketTimeout-loop.patch | 35 - ...63-reset-runstatus-in-redrive-thread.patch | 77 -- .../osg/bbockelm-defer_clientauth_v5_v2.patch | 0 xrootd/osg/xrootd.spec | 18 +- xrootd/upstream/xrootd.tarball.source | 4 +- 7 files changed, 35 insertions(+), 1288 deletions(-) delete mode 100644 xrootd/osg/2300-stat-call-reduction.patch delete mode 100644 xrootd/osg/2357-fix-errSocketTimeout-loop.patch delete mode 100644 xrootd/osg/2363-reset-runstatus-in-redrive-thread.patch mode change 100755 => 100644 xrootd/osg/bbockelm-defer_clientauth_v5_v2.patch diff --git a/xrootd/osg/2300-stat-call-reduction.patch b/xrootd/osg/2300-stat-call-reduction.patch deleted file mode 100644 index 0d7f9bf81..000000000 --- a/xrootd/osg/2300-stat-call-reduction.patch +++ /dev/null @@ -1,1114 +0,0 @@ -From 17af84fb2c53a48de7f622b8ac058ca92c1f6fb8 Mon Sep 17 00:00:00 2001 -From: Brian Bockelman -Date: Tue, 23 Jul 2024 13:49:38 -0400 -Subject: [PATCH 1/2] Revamp the GET state machine to avoid stat. - -The original GET implementation always started with a standalone `stat` -call - whose results were only used to determine whether to invoke -the directory listing code. - -This changes the GET to try to open the path by default and, if it -is either a directory descriptor (or fails with EISDIR), then try -doing the directory listing. - -By avoiding the standalone stat, we significantly reduce the number -of filesystem calls done under high concurrency (as the OFS code will -collapse all the open files into a single file handle). ---- - src/XrdHttp/XrdHttpReq.cc | 941 ++++++++++++++++++-------------------- - src/XrdHttp/XrdHttpReq.hh | 8 + - 2 files changed, 461 insertions(+), 488 deletions(-) - -diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc -index 35417c8ed88..ecf0fa23e1f 100644 ---- a/src/XrdHttp/XrdHttpReq.cc -+++ b/src/XrdHttp/XrdHttpReq.cc -@@ -521,8 +521,10 @@ bool XrdHttpReq::Error(XrdXrootd::Bridge::Context &info, //!< the result context - - if (PostProcessHTTPReq()) reset(); - -- // Second part of the ugly hack on stat() -- if ((request == rtGET) && (xrdreq.header.requestid == ntohs(kXR_stat))) -+ // If we are servicing a GET on a directory, it'll generate an error for the default -+ // OSS (we don't assume this is always true). Catch and suppress the error so we can instead -+ // generate a directory listing (if configured). -+ if ((request == rtGET) && (xrdreq.header.requestid == ntohs(kXR_open)) && (xrderrcode == kXR_isDirectory)) - return true; - - return false; -@@ -1093,22 +1095,36 @@ int XrdHttpReq::ProcessHTTPReq() { - } - - // The reqstate parameter basically moves us through a simple state machine. -- // - 0: Perform a stat on the resource -+ // To optimize things, we start off by opening the file; if it turns out to be a directory, then -+ // we close the file handle and switch to doing a HTML-based rendering of the directory. This -+ // avoids needing to always to do "stat" first to determine the next step (since the file-open also -+ // does a "stat"). -+ // - 0: Perform an open on the resource - // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped) -- // - 2: Perform an open request (dirlist as appropriate). -- // - 3+: Reads from file; if at end, perform a close. -+ // - 2: Perform a close (for dirlist only) -+ // - 3: Perform a dirlist. -+ // - 4+: Reads from file; if at end, perform a close. - switch (reqstate) { -- case 0: // Stat() -- -- // Do a Stat -- if (prot->doStat((char *) resourceplusopaque.c_str())) { -- XrdOucString errmsg = "Error stating"; -- errmsg += resource.c_str(); -- prot->SendSimpleResp(404, NULL, NULL, (char *) errmsg.c_str(), 0, false); -+ case 0: // Open the path for reading. -+ { -+ memset(&xrdreq, 0, sizeof (ClientRequest)); -+ xrdreq.open.requestid = htons(kXR_open); -+ l = resourceplusopaque.length() + 1; -+ xrdreq.open.dlen = htonl(l); -+ xrdreq.open.mode = 0; -+ xrdreq.open.options = htons(kXR_retstat | kXR_open_read); -+ -+ if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) { -+ prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false); - return -1; - } - -+ // Prepare to chunk up the request -+ writtenbytes = 0; -+ -+ // We want to be invoked again after this request is finished - return 0; -+ } - case 1: // Checksum request - if (!(fileflags & kXR_isDir) && !m_req_digest.empty()) { - // In this case, the Want-Digest header was set. -@@ -1138,16 +1154,23 @@ int XrdHttpReq::ProcessHTTPReq() { - reqstate += 1; - } - // fallthrough -- case 2: // Open() or dirlist -- { -+ case 2: // Close file handle for directory -+ if ((fileflags & kXR_isDir) && fopened) { -+ memset(&xrdreq, 0, sizeof (ClientRequest)); -+ xrdreq.close.requestid = htons(kXR_close); -+ memcpy(xrdreq.close.fhandle, fhandle, 4); - -- if (!prot->Bridge) { -- prot->SendSimpleResp(500, NULL, NULL, (char *) "prot->Bridge is NULL.", 0, false); -+ if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) { -+ prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run close request.", 0, false); - return -1; - } -- -+ return 0; -+ } else { -+ reqstate += 1; -+ } -+ // fallthrough -+ case 3: // List directory - if (fileflags & kXR_isDir) { -- - if (prot->listdeny) { - prot->SendSimpleResp(503, NULL, NULL, (char *) "Listings are disabled.", 0, false); - return -1; -@@ -1167,10 +1190,8 @@ int XrdHttpReq::ProcessHTTPReq() { - return -1; - } - -- - std::string res; - res = resourceplusopaque.c_str(); -- //res += "?xrd.dirstat=1"; - - // --------- DIRLIST - memset(&xrdreq, 0, sizeof (ClientRequest)); -@@ -1186,37 +1207,21 @@ int XrdHttpReq::ProcessHTTPReq() { - - // We don't want to be invoked again after this request is finished - return 1; -- - } - else { -- -- -- // --------- OPEN -- memset(&xrdreq, 0, sizeof (ClientRequest)); -- xrdreq.open.requestid = htons(kXR_open); -- l = resourceplusopaque.length() + 1; -- xrdreq.open.dlen = htonl(l); -- xrdreq.open.mode = 0; -- xrdreq.open.options = htons(kXR_retstat | kXR_open_read); -- -- if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) { -- prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false); -- return -1; -- } -- -- // Prepare to chunk up the request -- writtenbytes = 0; -- -- // We want to be invoked again after this request is finished -- return 0; -+ reqstate += 1; -+ } -+ // fallthrough -+ case 4: -+ { -+ auto retval = ReturnGetHeaders(); -+ if (retval) { -+ return retval; - } -- -- - } - // fallthrough -- default: // Read() or Close(); reqstate is 3+ -+ default: // Read() or Close(); reqstate is 4+ - { -- - const XrdHttpIOList &readChunkList = readRangeHandler.NextReadList(); - - // Close() if we have finished, otherwise read the next chunk -@@ -1788,6 +1793,287 @@ XrdHttpReq::PostProcessChecksum(std::string &digest_header) { - } - } - -+int -+XrdHttpReq::PostProcessListing(bool final_) { -+ -+ if (xrdresp == kXR_error) { -+ prot->SendSimpleResp(httpStatusCode, NULL, NULL, -+ httpStatusText.c_str(), httpStatusText.length(), false); -+ return -1; -+ } -+ -+ if (stringresp.empty()) { -+ // Start building the HTML response -+ stringresp = "\n" -+ "\n" -+ "\n" -+ "\n" -+ "\n" -+ "\n"; -+ -+ stringresp += ""; -+ stringresp += resource.c_str(); -+ stringresp += "\n"; -+ -+ stringresp += "\n" -+ "\n"; -+ -+ char *estr = escapeXML(resource.c_str()); -+ -+ stringresp += "

Listing of: "; -+ stringresp += estr; -+ stringresp += "

\n"; -+ -+ free(estr); -+ -+ stringresp += "
"; -+ -+ stringresp += "\n" -+ "\n" -+ "" -+ "" -+ "" -+ "" -+ "" -+ "\n"; -+ } -+ -+ // Now parse the answer building the entries vector -+ if (iovN > 0) { -+ char *startp = (char *) iovP[0].iov_base, *endp = 0; -+ char entry[1024]; -+ DirListInfo e; -+ while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) { -+ // Find the filename, it comes before the \n -+ if ((endp = (char *) strchr((const char*) startp, '\n'))) { -+ strncpy(entry, (char *) startp, endp - startp); -+ entry[endp - startp] = 0; -+ e.path = entry; -+ -+ endp++; -+ -+ // Now parse the stat info -+ TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry -+ << " stat=" << endp); -+ -+ long dummyl; -+ sscanf(endp, "%ld %lld %ld %ld", -+ &dummyl, -+ &e.size, -+ &e.flags, -+ &e.modtime); -+ } else -+ strcpy(entry, (char *) startp); -+ -+ if (e.path.length() && (e.path != ".") && (e.path != "..")) { -+ // The entry is filled. -+ std::string p = "" -+ ""; -+ p += "" -+ "" -+ "" -+ ""; -+ -+ stringresp += p; -+ } -+ -+ if (endp) { -+ char *pp = (char *)strchr((const char *)endp, '\n'); -+ if (pp) startp = pp+1; -+ else break; -+ } else break; -+ -+ } -+ } -+ -+ // If this was the last bunch of entries, send the buffer and empty it immediately -+ if (final_) { -+ stringresp += "
ModeFlagsSizeModifiedName
file1.txt
"; -+ -+ if (e.flags & kXR_isDir) p += "d"; -+ else p += "-"; -+ -+ if (e.flags & kXR_other) p += "o"; -+ else p += "-"; -+ -+ if (e.flags & kXR_offline) p += "O"; -+ else p += "-"; -+ -+ if (e.flags & kXR_readable) p += "r"; -+ else p += "-"; -+ -+ if (e.flags & kXR_writable) p += "w"; -+ else p += "-"; -+ -+ if (e.flags & kXR_xset) p += "x"; -+ else p += "-"; -+ -+ p += "" + itos(e.flags) + "" + itos(e.size) + "" + ISOdatetime(e.modtime) + "" -+ ""; -+ p += e.path; -+ -+ free(estr); -+ -+ p += "



" -+ "

Request by "; -+ -+ if (prot->SecEntity.name) -+ stringresp += prot->SecEntity.name; -+ else -+ stringresp += prot->Link->ID; -+ -+ if (prot->SecEntity.vorg || -+ prot->SecEntity.name || -+ prot->SecEntity.moninfo || -+ prot->SecEntity.role) -+ stringresp += " ("; -+ -+ if (prot->SecEntity.vorg) { -+ stringresp += " VO: "; -+ stringresp += prot->SecEntity.vorg; -+ } -+ -+ if (prot->SecEntity.moninfo) { -+ stringresp += " DN: "; -+ stringresp += prot->SecEntity.moninfo; -+ } else -+ if (prot->SecEntity.name) { -+ stringresp += " DN: "; -+ stringresp += prot->SecEntity.name; -+ } -+ -+ if (prot->SecEntity.role) { -+ stringresp += " Role: "; -+ stringresp += prot->SecEntity.role; -+ if (prot->SecEntity.endorsements) { -+ stringresp += " ("; -+ stringresp += prot->SecEntity.endorsements; -+ stringresp += ") "; -+ } -+ } -+ -+ if (prot->SecEntity.vorg || -+ prot->SecEntity.moninfo || -+ prot->SecEntity.role) -+ stringresp += " )"; -+ -+ if (prot->SecEntity.host) { -+ stringresp += " ( "; -+ stringresp += prot->SecEntity.host; -+ stringresp += " )"; -+ } -+ -+ stringresp += "

\n"; -+ stringresp += "

Powered by XrdHTTP "; -+ stringresp += XrdVSTRING; -+ stringresp += " (CERN IT-SDC)

\n"; -+ -+ prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive); -+ stringresp.clear(); -+ return keepalive ? 1 : -1; -+ } -+ -+ return 1; -+} -+ -+int -+XrdHttpReq::ReturnGetHeaders() { -+ std::string responseHeader; -+ if (!m_digest_header.empty()) { -+ responseHeader = m_digest_header; -+ } -+ long one; -+ if (filemodtime && XrdOucEnv::Import("XRDPFC", one)) { -+ if (!responseHeader.empty()) { -+ responseHeader += "\r\n"; -+ } -+ long object_age = time(NULL) - filemodtime; -+ responseHeader += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age); -+ } -+ -+ const XrdHttpReadRangeHandler::UserRangeList &uranges = readRangeHandler.ListResolvedRanges(); -+ if (uranges.empty() && readRangeHandler.getError()) { -+ prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false); -+ return -1; -+ } -+ -+ if (readRangeHandler.isFullFile()) { -+ // Full file. -+ TRACEI(REQ, "Sending full file: " << filesize); -+ if (m_transfer_encoding_chunked && m_trailer_headers) { -+ prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive); -+ } else { -+ prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive); -+ } -+ return 0; -+ } -+ -+ if (readRangeHandler.isSingleRange()) { -+ // Possibly with zero sized file but should have been included -+ // in the FullFile case above -+ if (uranges.size() != 1) -+ return -1; -+ -+ // Only one range to return to the user -+ char buf[64]; -+ const off_t cnt = uranges[0].end - uranges[0].start + 1; -+ -+ XrdOucString s = "Content-Range: bytes "; -+ sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize); -+ s += buf; -+ if (!responseHeader.empty()) { -+ s += "\r\n"; -+ s += responseHeader.c_str(); -+ } -+ -+ if (m_transfer_encoding_chunked && m_trailer_headers) { -+ prot->StartChunkedResp(206, NULL, (char *)s.c_str(), -1, keepalive); -+ } else { -+ prot->SendSimpleResp(206, NULL, (char *)s.c_str(), NULL, cnt, keepalive); -+ } -+ return 0; -+ } -+ -+ // Multiple reads to perform, compose and send the header -+ off_t cnt = 0; -+ for (auto &ur : uranges) { -+ cnt += ur.end - ur.start + 1; -+ -+ cnt += buildPartialHdr(ur.start, -+ ur.end, -+ filesize, -+ (char *) "123456").size(); -+ -+ } -+ cnt += buildPartialHdrEnd((char *) "123456").size(); -+ std::string header = "Content-Type: multipart/byteranges; boundary=123456"; -+ if (!m_digest_header.empty()) { -+ header += "\n"; -+ header += m_digest_header; -+ } -+ -+ if (m_transfer_encoding_chunked && m_trailer_headers) { -+ prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive); -+ } else { -+ prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive); -+ } -+ return 0; -+} - - // This is invoked by the callbacks, after something has happened in the bridge - -@@ -1867,488 +2153,167 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { - } - case XrdHttpReq::rtGET: - { -+ // To duplicate the state diagram from the rtGET request state -+ // - 0: Perform an open request -+ // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped) -+ // - 2: Perform a close (for directory listings only) -+ // - 3: Perform a dirlist -+ // - 4+: Reads from file; if at end, perform a close. -+ switch (reqstate) { -+ case 0: // open -+ { -+ if (xrdresp == kXR_ok) { -+ fopened = true; -+ getfhandle(); - -- if (xrdreq.header.requestid == ntohs(kXR_dirlist)) { -- -- -- if (xrdresp == kXR_error) { -- prot->SendSimpleResp(httpStatusCode, NULL, NULL, -- httpStatusText.c_str(), httpStatusText.length(), false); -- return -1; -- } -- -- -- if (stringresp.empty()) { -- -- // Start building the HTML response -- stringresp = "\n" -- "\n" -- "\n" -- "\n" -- "\n" -- "\n"; -- -- stringresp += ""; -- stringresp += resource.c_str(); -- stringresp += "\n"; -- -- stringresp += "\n" -- "\n"; -- -- char *estr = escapeXML(resource.c_str()); -- -- stringresp += "

Listing of: "; -- stringresp += estr; -- stringresp += "

\n"; -- -- free(estr); -- -- stringresp += "
"; -- -- -- stringresp += "\n" -- "\n" -- "" -- "" -- "" -- "" -- "" -- "\n"; -- -- } -- -- // Now parse the answer building the entries vector -- if (iovN > 0) { -- char *startp = (char *) iovP[0].iov_base, *endp = 0; -- char entry[1024]; -- DirListInfo e; -- while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) { -- // Find the filename, it comes before the \n -- if ((endp = (char *) strchr((const char*) startp, '\n'))) { -- strncpy(entry, (char *) startp, endp - startp); -- entry[endp - startp] = 0; -- e.path = entry; -- -- endp++; -- -- // Now parse the stat info -- TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry -- << " stat=" << endp); -+ // Always try to parse response. In the case of a caching proxy, the open -+ // will have created the file in cache -+ if (iovP[1].iov_len > 1) { -+ TRACEI(REQ, "Stat for GET " << resource.c_str() -+ << " stat=" << (char *) iovP[1].iov_base); - - long dummyl; -- sscanf(endp, "%ld %lld %ld %ld", -- &dummyl, -- &e.size, -- &e.flags, -- &e.modtime); -- } else -- strcpy(entry, (char *) startp); -- -- -- if (e.path.length() && (e.path != ".") && (e.path != "..")) { -- // The entry is filled. -- std::string p = "" -- ""; -- p += "" -- "" -- "" -- ""; -- -- stringresp += p; - -+ readRangeHandler.SetFilesize(filesize); - -+ // As above: if the client specified a response size, we use that. -+ // Otherwise, utilize the filesize -+ if (!length) { -+ length = filesize; -+ } - } -+ else { -+ TRACEI(ALL, "GET returned no STAT information. Internal error?"); -+ prot->SendSimpleResp(500, NULL, NULL, "Storage system did not return stat info.", 0, false); -+ return -1; -+ } -+ return 0; -+ } else if (xrderrcode == kXR_isDirectory) { // This is a directory; trigger directory-handling topic. -+ fileflags = kXR_isDir; -+ return 0; -+ } else { // xrdresp indicates an error occurred - -- -- if (endp) { -- char *pp = (char *)strchr((const char *)endp, '\n'); -- if (pp) startp = pp+1; -- else break; -- } else break; -- -+ prot->SendSimpleResp(httpStatusCode, NULL, NULL, -+ httpStatusText.c_str(), httpStatusText.length(), false); -+ return -1; - } -+ // Case should not be reachable -+ return -1; -+ } // end open -+ case 1: // checksum was requested and now we have its response. -+ { -+ return PostProcessChecksum(m_digest_header); - } -- -- // If this was the last bunch of entries, send the buffer and empty it immediately -- if (final_) { -- stringresp += "
ModeFlagsSizeModifiedName
file1.txt
"; -- -- if (e.flags & kXR_isDir) p += "d"; -- else p += "-"; -- -- if (e.flags & kXR_other) p += "o"; -- else p += "-"; -- -- if (e.flags & kXR_offline) p += "O"; -- else p += "-"; -- -- if (e.flags & kXR_readable) p += "r"; -- else p += "-"; -- -- if (e.flags & kXR_writable) p += "w"; -- else p += "-"; -- -- if (e.flags & kXR_xset) p += "x"; -- else p += "-"; -- -- p += "" + itos(e.flags) + "" + itos(e.size) + "" + ISOdatetime(e.modtime) + "" -- ""; -- p += e.path; -- -- free(estr); -- -- p += "



" -- "

Request by "; -- -- if (prot->SecEntity.name) -- stringresp += prot->SecEntity.name; -- else -- stringresp += prot->Link->ID; -- -- if (prot->SecEntity.vorg || -- prot->SecEntity.name || -- prot->SecEntity.moninfo || -- prot->SecEntity.role) -- stringresp += " ("; -- -- if (prot->SecEntity.vorg) { -- stringresp += " VO: "; -- stringresp += prot->SecEntity.vorg; -- } -- -- if (prot->SecEntity.moninfo) { -- stringresp += " DN: "; -- stringresp += prot->SecEntity.moninfo; -- } else -- if (prot->SecEntity.name) { -- stringresp += " DN: "; -- stringresp += prot->SecEntity.name; -- } -- -- -- if (prot->SecEntity.role) { -- stringresp += " Role: "; -- stringresp += prot->SecEntity.role; -- if (prot->SecEntity.endorsements) { -- stringresp += " ("; -- stringresp += prot->SecEntity.endorsements; -- stringresp += ") "; -- } -- } -- -- -- -- if (prot->SecEntity.vorg || -- prot->SecEntity.moninfo || -- prot->SecEntity.role) -- stringresp += " )"; -- -- if (prot->SecEntity.host) { -- stringresp += " ( "; -- stringresp += prot->SecEntity.host; -- stringresp += " )"; -+ case 2: // close file handle in case of the directory -+ { -+ if (xrdresp != kXR_ok) { -+ prot->SendSimpleResp(httpStatusCode, NULL, NULL, -+ httpStatusText.c_str(), httpStatusText.length(), false); -+ return -1; - } -- -- stringresp += "

\n"; -- stringresp += "

Powered by XrdHTTP "; -- stringresp += XrdVSTRING; -- stringresp += " (CERN IT-SDC)

\n"; -- -- prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive); -- stringresp.clear(); -- return keepalive ? 1 : -1; -+ return 0; - } -- -- -- } // end handling of dirlist -- else -- { // begin handling of open-read-close -- -- // To duplicate the state diagram from the rtGET request state -- // - 0: Perform a stat on the resource -- // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped) -- // - 2: Perform an open request (dirlist as appropriate). -- // - 3+: Reads from file; if at end, perform a close. -- switch (reqstate) { -- case 0: //stat -+ case 3: // handle the directory listing response -+ { -+ return PostProcessListing(final_); -+ } -+ default: //read or readv, followed by a close. -+ { -+ // If we are postprocessing a close, potentially send out informational trailers -+ if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing) - { -- // Ugly hack. Be careful with EOS! Test with vanilla XrdHTTP and EOS, separately -- // A 404 on the preliminary stat() is fatal only -- // in a manager. A non-manager will ignore the result and try anyway to open the file -- // -- if (xrdresp == kXR_ok) { -- -- if (iovN > 0) { -- -- // Now parse the stat info -- TRACEI(REQ, "Stat for GET " << resource.c_str() -- << " stat=" << (char *) iovP[0].iov_base); -- -- long dummyl; -- sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld", -- &dummyl, -- &filesize, -- &fileflags, -- &filemodtime); -- -- readRangeHandler.SetFilesize(filesize); -- -- // We will default the response size specified by the headers; if that -- // wasn't given, use the file size. -- if (!length) { -- length = filesize; -- } -- } -- else { -- TRACEI(REQ, "Can't find the stat information for '" -- << resource.c_str() << "' Internal error?"); -- } -- } -- -- // We are here if the request failed -- -- if (prot->myRole == kXR_isManager) { -- prot->SendSimpleResp(httpStatusCode, NULL, NULL, -- httpStatusText.c_str(), httpStatusText.length(), false); -- return -1; -+ const XrdHttpReadRangeHandler::Error &rrerror = readRangeHandler.getError(); -+ if (rrerror) { -+ httpStatusCode = rrerror.httpRetCode; -+ httpStatusText = rrerror.errMsg; - } -- -- // We are here in the case of a negative response in a non-manager -- -- return 0; -- } // end stat -- case 1: // checksum was requested and now we have its response. -- { -- return PostProcessChecksum(m_digest_header); -- } -- case 2: // open -- { -- if (xrdresp == kXR_ok) { -- -- getfhandle(); - -- // Always try to parse response. In the case of a caching proxy, the open -- // will have created the file in cache -- if (iovP[1].iov_len > 1) { -- TRACEI(REQ, "Stat for GET " << resource.c_str() -- << " stat=" << (char *) iovP[1].iov_base); -- -- long dummyl; -- sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld", -- &dummyl, -- &filesize, -- &fileflags, -- &filemodtime); -- -- readRangeHandler.SetFilesize(filesize); -- -- // As above: if the client specified a response size, we use that. -- // Otherwise, utilize the filesize -- if (!length) { -- length = filesize; -- } -- } -- else { -- TRACEI(ALL, "GET returned no STAT information. Internal error?"); -- } -- -- std::string responseHeader; -- if (!m_digest_header.empty()) { -- responseHeader = m_digest_header; -- } -- long one; -- if (filemodtime && XrdOucEnv::Import("XRDPFC", one)) { -- if (!responseHeader.empty()) { -- responseHeader += "\r\n"; -- } -- long object_age = time(NULL) - filemodtime; -- responseHeader += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age); -- } -- -- const XrdHttpReadRangeHandler::UserRangeList &uranges = readRangeHandler.ListResolvedRanges(); -- if (uranges.empty() && readRangeHandler.getError()) { -- prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false); -+ if (m_transfer_encoding_chunked && m_trailer_headers) { -+ if (prot->ChunkRespHeader(0)) - return -1; -- } -- -- if (readRangeHandler.isFullFile()) { -- // Full file. -- -- if (m_transfer_encoding_chunked && m_trailer_headers) { -- prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive); -- } else { -- prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive); -- } -- return 0; -- } - -- if (readRangeHandler.isSingleRange()) { -- // Possibly with zero sized file but should have been included -- // in the FullFile case above -- if (uranges.size() != 1) -- return -1; -- -- // Only one range to return to the user -- char buf[64]; -- const off_t cnt = uranges[0].end - uranges[0].start + 1; -- -- XrdOucString s = "Content-Range: bytes "; -- sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize); -- s += buf; -- if (!responseHeader.empty()) { -- s += "\r\n"; -- s += responseHeader.c_str(); -- } -- -- if (m_transfer_encoding_chunked && m_trailer_headers) { -- prot->StartChunkedResp(206, NULL, (char *)s.c_str(), -1, keepalive); -- } else { -- prot->SendSimpleResp(206, NULL, (char *)s.c_str(), NULL, cnt, keepalive); -- } -- return 0; -- } -- -- // Multiple reads to perform, compose and send the header -- off_t cnt = 0; -- for (auto &ur : uranges) { -- cnt += ur.end - ur.start + 1; -- -- cnt += buildPartialHdr(ur.start, -- ur.end, -- filesize, -- (char *) "123456").size(); -- -- } -- cnt += buildPartialHdrEnd((char *) "123456").size(); -- std::string header = "Content-Type: multipart/byteranges; boundary=123456"; -- if (!m_digest_header.empty()) { -- header += "\n"; -- header += m_digest_header; -- } -- -- if (m_transfer_encoding_chunked && m_trailer_headers) { -- prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive); -- } else { -- prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive); -- } -- return 0; -+ const std::string crlf = "\r\n"; -+ std::stringstream ss; -+ ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf; - -+ const auto header = ss.str(); -+ if (prot->SendData(header.c_str(), header.size())) -+ return -1; - -- } else { // xrdresp indicates an error occurred -- -- prot->SendSimpleResp(httpStatusCode, NULL, NULL, -- httpStatusText.c_str(), httpStatusText.length(), false); -- return -1; -+ if (prot->ChunkRespFooter()) -+ return -1; - } - -- // Case should not be reachable -- return -1; -+ if (rrerror) return -1; -+ return keepalive ? 1 : -1; - } -- default: //read or readv -- { -- // If we are postprocessing a close, potentially send out informational trailers -- if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing) -- { -- const XrdHttpReadRangeHandler::Error &rrerror = readRangeHandler.getError(); -- if (rrerror) { -- httpStatusCode = rrerror.httpRetCode; -- httpStatusText = rrerror.errMsg; -- } -- -- if (m_transfer_encoding_chunked && m_trailer_headers) { -- if (prot->ChunkRespHeader(0)) -- return -1; -- -- const std::string crlf = "\r\n"; -- std::stringstream ss; -- ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf; -- -- const auto header = ss.str(); -- if (prot->SendData(header.c_str(), header.size())) -- return -1; -- -- if (prot->ChunkRespFooter()) -- return -1; -- } -- -- if (rrerror) return -1; -- return keepalive ? 1 : -1; -- } -- -- // On error, we can only send out a message if trailers are enabled and the -- // status response in trailer behavior is requested. -- if (xrdresp == kXR_error) { -- if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) { -- // A trailer header is appropriate in this case; this is signified by -- // a chunk with size zero, then the trailer, then a crlf. -- // -- // We only send the status trailer when explicitly requested; otherwise a -- // "normal" HTTP client might simply see a short response and think it's a -- // success -- if (prot->ChunkRespHeader(0)) -- return -1; -- -- const std::string crlf = "\r\n"; -- std::stringstream ss; -- ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf; - -- const auto header = ss.str(); -- if (prot->SendData(header.c_str(), header.size())) -- return -1; -- -- if (prot->ChunkRespFooter()) -- return -1; -- -- return -1; -- } else { -+ // On error, we can only send out a message if trailers are enabled and the -+ // status response in trailer behavior is requested. -+ if (xrdresp == kXR_error) { -+ if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) { -+ // A trailer header is appropriate in this case; this is signified by -+ // a chunk with size zero, then the trailer, then a crlf. -+ // -+ // We only send the status trailer when explicitly requested; otherwise a -+ // "normal" HTTP client might simply see a short response and think it's a -+ // success -+ if (prot->ChunkRespHeader(0)) - return -1; -- } -- } - -+ const std::string crlf = "\r\n"; -+ std::stringstream ss; -+ ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf; - -- TRACEI(REQ, "Got data vectors to send:" << iovN); -+ const auto header = ss.str(); -+ if (prot->SendData(header.c_str(), header.size())) -+ return -1; - -- XrdHttpIOList received; -- getReadResponse(received); -+ if (prot->ChunkRespFooter()) -+ return -1; - -- int rc; -- if (readRangeHandler.isSingleRange()) { -- rc = sendReadResponseSingleRange(received); -+ return -1; - } else { -- rc = sendReadResponsesMultiRanges(received); -- } -- if (rc) { -- // make sure readRangeHandler will trigger close -- // of file after next NextReadList(). -- readRangeHandler.NotifyError(); -+ return -1; - } -+ } - -- return 0; -- } // end read or readv - -- } // switch reqstate -+ TRACEI(REQ, "Got data vectors to send:" << iovN); - -- } // End handling of the open-read-close case -+ XrdHttpIOList received; -+ getReadResponse(received); - -+ int rc; -+ if (readRangeHandler.isSingleRange()) { -+ rc = sendReadResponseSingleRange(received); -+ } else { -+ rc = sendReadResponsesMultiRanges(received); -+ } -+ if (rc) { -+ // make sure readRangeHandler will trigger close -+ // of file after next NextReadList(). -+ readRangeHandler.NotifyError(); -+ } - -+ return 0; -+ } // end read or readv -+ -+ } // switch reqstate - break; - } // case GET - -- - case XrdHttpReq::rtPUT: - { - if (!fopened) { -diff --git a/src/XrdHttp/XrdHttpReq.hh b/src/XrdHttp/XrdHttpReq.hh -index ed56ede2066..7b368b506f5 100644 ---- a/src/XrdHttp/XrdHttpReq.hh -+++ b/src/XrdHttp/XrdHttpReq.hh -@@ -107,6 +107,14 @@ private: - // be included in the response. - int PostProcessChecksum(std::string &digest_header); - -+ // Process the listing request of a GET request against a directory -+ // - final_: True if this is the last entry in the listing. -+ int PostProcessListing(bool final_); -+ -+ // Send the response for a GET request for a file read (i.e., not a directory) -+ // Invoked after the open is successful but before the first read is issued. -+ int ReturnGetHeaders(); -+ - /// Cook and send the response after the bridge did something - /// Return values: - /// 0->everything OK, additionsl steps may be required - -From a9143716197a24bd9795109bdd2457f0f5cf2e84 Mon Sep 17 00:00:00 2001 -From: Fabrizio Furano -Date: Fri, 13 Sep 2024 11:19:51 +0200 -Subject: [PATCH 2/2] PostProcessListing should return 0, not 1 - ---- - src/XrdHttp/XrdHttpReq.cc | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc -index ecf0fa23e1f..c7c2b376e91 100644 ---- a/src/XrdHttp/XrdHttpReq.cc -+++ b/src/XrdHttp/XrdHttpReq.cc -@@ -1988,7 +1988,7 @@ XrdHttpReq::PostProcessListing(bool final_) { - return keepalive ? 1 : -1; - } - -- return 1; -+ return 0; - } - - int diff --git a/xrootd/osg/2348-cache-age-logic.patch b/xrootd/osg/2348-cache-age-logic.patch index 11e619575..3459b46fb 100644 --- a/xrootd/osg/2348-cache-age-logic.patch +++ b/xrootd/osg/2348-cache-age-logic.patch @@ -1,59 +1,30 @@ -From 4e2aeff330247bb99ac7b9f57fc457f840405c5f Mon Sep 17 00:00:00 2001 +From 2160a23febe1782cc3590a473209f0e74f965084 Mon Sep 17 00:00:00 2001 From: Brian Bockelman -Date: Sun, 29 Sep 2024 11:36:29 +0200 -Subject: [PATCH] Change cache age logic to only indicate fully cached files +Date: Tue, 3 Dec 2024 10:19:53 -0600 +Subject: [PATCH] [XrdPfc] Zero mtime if object is not cached -The cache mtime logic (whose sole use is to have the `Age` header -set) was fairly misleading: a `HEAD` (or `stat`) request would cause -the cinfo file to be written, and the cache to indicate it had the -data. +XrdHttp determines the presence of the `Age` header (indicating data +that is cached) based on whether the `mtime` is zero; however, XrdPfc +sets `ctime` to 0 if things aren't cached. -This was not useful: the object was indicated as cached even when -not a single byte of data was present. - -This takes the opposite approach: mtime and ctime are only set -(and hence the `Age` header is returned) only if the file is complete. +Also set `mtime` to 0 in the not-cached case. --- - src/XrdPfc/XrdPfcIOFile.cc | 22 +++++++++++++++------- - 1 file changed, 15 insertions(+), 7 deletions(-) + src/XrdPfc/XrdPfc.cc | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) -diff --git a/src/XrdPfc/XrdPfcIOFile.cc b/src/XrdPfc/XrdPfcIOFile.cc -index d0b3ebef169..68d1ac3f5ae 100644 ---- a/src/XrdPfc/XrdPfcIOFile.cc -+++ b/src/XrdPfc/XrdPfcIOFile.cc -@@ -96,9 +96,18 @@ int IOFile::initCachedStat() - // The filesize from the file itself may be misleading if its download is incomplete; take it from the cinfo. - tmpStat.st_size = info.GetFileSize(); - // We are arguably abusing the mtime to be the creation time of the file; then ctime becomes the -- // last time additional data was cached. -- tmpStat.st_mtime = info.GetCreationTime(); -- TRACEIO(Info, trace_pfx << "successfully read size " << tmpStat.st_size << " and creation time " << tmpStat.st_mtime << " from info file"); -+ // last time additional data was cached. Note we only set the mtime if the file is complete; this -+ // helps clients know whether there was a cache hit or miss. -+ if (info.IsComplete()) -+ { -+ tmpStat.st_mtime = info.GetCreationTime(); -+ TRACEIO(Info, trace_pfx << "successfully read size " << tmpStat.st_size << " and creation time " << tmpStat.st_mtime << " from info file"); -+ } -+ else -+ { -+ tmpStat.st_mtime = 0; -+ TRACEIO(Info, trace_pfx << "successfully read size from info file = " << tmpStat.st_size); -+ } - res = 0; - } - else -@@ -120,10 +129,9 @@ int IOFile::initCachedStat() - res = GetInput()->Fstat(tmpStat); - TRACEIO(Debug, trace_pfx << "got stat from client res = " << res << ", size = " << tmpStat.st_size); - // The mtime / atime / ctime for cached responses come from the file on disk in the cache hit case. -- // To avoid weirdness when two subsequent stat queries can give wildly divergent times (one from the -- // origin, one from the cache), set the times to "now" so we effectively only report the *time as the -- // cache service sees it. -- tmpStat.st_ctime = tmpStat.st_mtime = tmpStat.st_atime = time(NULL); -+ // In the cache miss cache, we set the various *time attributes to 0 as they are meant to indicate -+ // the age of the cache information and 0 indicates non-existence. -+ tmpStat.st_ctime = tmpStat.st_mtime = tmpStat.st_atime = 0; +diff --git a/src/XrdPfc/XrdPfc.cc b/src/XrdPfc/XrdPfc.cc +index 567bda0d9a2..86078fbdf6b 100644 +--- a/src/XrdPfc/XrdPfc.cc ++++ b/src/XrdPfc/XrdPfc.cc +@@ -1143,8 +1143,9 @@ int Cache::Stat(const char *curl, struct stat &sbuff) } + sbuff.st_size = file_size; + bool is_cached = DecideIfConsideredCached(file_size, sbuff.st_blocks * 512ll); +- if ( ! is_cached) +- sbuff.st_atime = 0; ++ if ( ! is_cached) { ++ sbuff.st_ctime = sbuff.st_mtime = sbuff.st_atime = 0; ++ } + + TRACE(Debug, tpfx << "from disk " << curl << " -> " << res); - if (res == 0) diff --git a/xrootd/osg/2357-fix-errSocketTimeout-loop.patch b/xrootd/osg/2357-fix-errSocketTimeout-loop.patch deleted file mode 100644 index 03e223e5a..000000000 --- a/xrootd/osg/2357-fix-errSocketTimeout-loop.patch +++ /dev/null @@ -1,35 +0,0 @@ -From eb4295e0a2fc17c2210186424f949001eb4af93f Mon Sep 17 00:00:00 2001 -From: Andrew Hanushevsky -Date: Wed, 16 Oct 2024 04:41:33 -0700 -Subject: [PATCH] [Server] Fix for improper error handling for current R5 also - fixes #2357 - ---- - src/XrdOfs/XrdOfs.cc | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/src/XrdOfs/XrdOfs.cc b/src/XrdOfs/XrdOfs.cc -index cb8d89cdaf0..e91d728b487 100644 ---- a/src/XrdOfs/XrdOfs.cc -+++ b/src/XrdOfs/XrdOfs.cc -@@ -2549,12 +2549,19 @@ int XrdOfs::Emsg(const char *pfx, // Message prefix value - // If the error is EBUSY then we just need to stall the client. This is - // a hack in order to provide for proxy support - // -+// The hack unfotunately is now beinng triggered for reads and writes when -+// it was never so before (presumably due to client changes). So do not -+// apply the hack for these operations. This gets a better fix in R 6.0 -+// -+if (strcmp("read", op) && strcmp("readv", op) && strcmp("pgRead", op) && -+ strcmp("write",op) && strcmp("pgwrite",op)) { - if (ecode < 0) ecode = -ecode; - if (ecode == EBUSY) return 5; // A hack for proxy support - - // Check for timeout conditions that require a client delay - // - if (ecode == ETIMEDOUT) return OSSDelay; -+ } - - // Format the error message - // - diff --git a/xrootd/osg/2363-reset-runstatus-in-redrive-thread.patch b/xrootd/osg/2363-reset-runstatus-in-redrive-thread.patch deleted file mode 100644 index ad616a3ac..000000000 --- a/xrootd/osg/2363-reset-runstatus-in-redrive-thread.patch +++ /dev/null @@ -1,77 +0,0 @@ -From b9d604c5b4801e09c4cc8cdbdab195f2dd7fac8c Mon Sep 17 00:00:00 2001 -From: Brian Bockelman -Date: Thu, 24 Oct 2024 10:55:33 -0500 -Subject: [PATCH] Reset the state after processing while in redrive - -Each time we process successfully from the redrive thread, we should -decrement the status counter. - -Otherwise, after multiple requests, the attempt to `Run` from XrdHttp -will fail. This was observed to cause failures in closing files -100% of the time when the open was delayed, causing failures in -the XrdHttp response. - -This additionally adds logging on failures for submitting the run. ---- - src/XrdXrootd/XrdXrootdTransit.cc | 22 ++++++++++++++++++---- - 1 file changed, 18 insertions(+), 4 deletions(-) - -diff --git a/src/XrdXrootd/XrdXrootdTransit.cc b/src/XrdXrootd/XrdXrootdTransit.cc -index 52d454de7c6..57720b78ac0 100644 ---- a/src/XrdXrootd/XrdXrootdTransit.cc -+++ b/src/XrdXrootd/XrdXrootdTransit.cc -@@ -463,6 +463,11 @@ void XrdXrootdTransit::Redrive() - if (rc == 0) { - rc = realProt->Process(NULL); - } -+ if (runStatus) -+ {AtomicBeg(runMutex); -+ AtomicZAP(runStatus); -+ AtomicEnd(runMutex); -+ } - } while((rc == 0) && !runError && !runWait); - } - else rc = Send(kXR_error, ioV, 2, 0); -@@ -567,7 +572,10 @@ bool XrdXrootdTransit::Run(const char *xreqP, char *xdataP, int xdataL) - AtomicBeg(runMutex); - rc = AtomicInc(runStatus); - AtomicEnd(runMutex); -- if (rc) return false; -+ if (rc) -+ {TRACEP(REQ, "Bridge request failed due to re-entry"); -+ return false; -+ } - - // Copy the request header - // -@@ -579,13 +587,17 @@ bool XrdXrootdTransit::Run(const char *xreqP, char *xdataP, int xdataL) - if (Request.header.requestid & 0x8000 - || Request.header.requestid > static_cast(kXR_truncate) - || !reqTab[Request.header.requestid - kXR_auth]) -- return Fail(kXR_Unsupported, "Unsupported bridge request"); -+ {TRACEP(REQ, "Unsupported bridge request"); -+ return Fail(kXR_Unsupported, "Unsupported bridge request"); -+ } - - // Validate the data length - // - Request.header.dlen = ntohl(Request.header.dlen); - if (Request.header.dlen < 0) -- return Fail(kXR_ArgInvalid, "Invalid request data length"); -+ {TRACEP(REQ, "Invalid request data length"); -+ return Fail(kXR_ArgInvalid, "Invalid request data length"); -+ } - - // Copy the stream id and trace this request - // -@@ -607,7 +619,9 @@ bool XrdXrootdTransit::Run(const char *xreqP, char *xdataP, int xdataL) - if (!runArgs || movLen > runABsz) - {if (runArgs) free(runArgs); - if (!(runArgs = (char *)malloc(movLen))) -- return Fail(kXR_NoMemory, "Insufficient memory"); -+ {TRACEP(REQ, "Failed to allocate memory"); -+ return Fail(kXR_NoMemory, "Insufficient memory"); -+ } - runABsz = movLen; - } - memcpy(runArgs, xdataP, movLen); runALen = movLen; diff --git a/xrootd/osg/bbockelm-defer_clientauth_v5_v2.patch b/xrootd/osg/bbockelm-defer_clientauth_v5_v2.patch old mode 100755 new mode 100644 diff --git a/xrootd/osg/xrootd.spec b/xrootd/osg/xrootd.spec index a47be7e3e..101cdab9b 100644 --- a/xrootd/osg/xrootd.spec +++ b/xrootd/osg/xrootd.spec @@ -79,8 +79,8 @@ #------------------------------------------------------------------------------- Name: xrootd Epoch: 1 -Version: 5.7.1 -Release: 1.4%{?dist}%{?_with_clang:.clang}%{?_with_asan:.asan} +Version: 5.7.2 +Release: 1.1%{?dist}%{?_with_clang:.clang}%{?_with_asan:.asan} Summary: Extended ROOT file server Group: System Environment/Daemons License: LGPLv3+ @@ -100,11 +100,8 @@ Source1: xrootd-%{compat_version}.tar.gz # OSG Patches not merged into upstream Patch0: bbockelm-defer_clientauth_v5_v2.patch Patch1: 1868-env-hostname-override.patch -Patch3: 2300-stat-call-reduction.patch Patch4: bbockelm-3-oss-statistics.patch Patch5: 2348-cache-age-logic.patch -Patch6: 2357-fix-errSocketTimeout-loop.patch -Patch7: 2363-reset-runstatus-in-redrive-thread.patch # Debug Patches Patch101: 0003-DEBUG-unset-use-pep517.patch @@ -527,11 +524,8 @@ This package contains compatibility binaries for xrootd 4 servers. cd %{build_dir} %patch0 -p1 %patch1 -p1 -%patch3 -p1 %patch4 -p1 %patch5 -p1 -%patch6 -p1 -%patch7 -p1 # %%patch101 -p1 cd .. @@ -1192,6 +1186,14 @@ fi # Changelog #------------------------------------------------------------------------------- %changelog +* Tue Dec 03 2024 Mátyás Selmeci - 5.7.2-1.1 +- Update to 5.7.2 and drop upstreamed patches (SOFTWARE-6036) + - Drop 2300-stat-call-reduction.patch + - Update 2348-cache-age-logic.patch + (replace with https://github.com/bbockelm/xrootd/commit/2160a23febe1782cc3590a473209f0e74f965084) + - Drop 2357-fix-errSocketTimeout-loop.patch + - Drop 2363-reset-runstatus-in-redrive-thread.patch + * Wed Oct 16 2024 Matt Westphall - 5.7.1-1.4 - Add 2363-reset-runstatus-in-redrive-thread.patch (SOFTWARE-6024) diff --git a/xrootd/upstream/xrootd.tarball.source b/xrootd/upstream/xrootd.tarball.source index 2c7e9ca3e..4e7f62f91 100644 --- a/xrootd/upstream/xrootd.tarball.source +++ b/xrootd/upstream/xrootd.tarball.source @@ -1,2 +1,2 @@ -xrootd/5.7.1/xrootd-5.7.1.tar.gz sha1sum=915169810932aab4ac17e32adcdc07276e325c89 -# https://github.com/xrootd/xrootd/releases/download/v5.7.1/xrootd-5.7.1.tar.gz +xrootd/5.7.2/xrootd-5.7.2.tar.gz sha1sum=6c7933c9e06514508ccfaa92bcc9344673707ecb +# https://github.com/xrootd/xrootd/releases/download/v5.7.2/xrootd-5.7.2.tar.gz