c3788110e8d865774fff8197cf5b47350472ba64 chmalee Tue Aug 4 11:26:55 2026 -0700 Fix hubSpace hook error handling and nested hub path handling, refs #37964 Co-Authored-By: Claude Opus 5 (1M context) diff --git src/hg/hgHubConnect/hooks/pre-create.c src/hg/hgHubConnect/hooks/pre-create.c index e2dbde8f761..c9d3967fa52 100644 --- src/hg/hgHubConnect/hooks/pre-create.c +++ src/hg/hgHubConnect/hooks/pre-create.c @@ -93,33 +93,32 @@ // our JS code also sets 'fileName' - try all to handle resumed uploads with old metadata) char *reqFileName = jsonQueryString(req, "", "Event.Upload.MetaData.fileName", NULL); if (!reqFileName) reqFileName = jsonQueryString(req, "", "Event.Upload.MetaData.filename", NULL); if (!reqFileName) reqFileName = jsonQueryString(req, "", "Event.Upload.MetaData.name", NULL); if (!reqFileName) { errAbort("No filename found in upload metadata (checked fileName, filename, and name)"); } char *reqParentDir = jsonQueryString(req, "", "Event.Upload.MetaData.parentDir", NULL); // Trim first, a hub name with a trailing space is impossible to spot in an error // message, then check what is left. The browser does this too, but hubtools and // any other tus client post here directly reqParentDir = normalizeParentDir(reqParentDir); - if (reqParentDir && isEmpty(reqParentDir)) - errAbort("Hub name for file '%s' is only whitespace, please give the hub a name", - reqFileName); + if (isEmpty(reqParentDir)) + errAbort("No hub name for file '%s', please give the hub a name", reqFileName); if (!isValidParentDir(reqParentDir)) errAbort("Hub name '%s' for file '%s' can only contain letters, numbers, periods " "and underscores, in '/' separated components. Please rename the hub.", reqParentDir, reqFileName); boolean isHubToolsUpload = FALSE; char *hubtoolsStr = jsonQueryString(req, "", "Event.Upload.MetaData.hubtools", NULL); if (hubtoolsStr) isHubToolsUpload = sameString(hubtoolsStr, "TRUE") || sameString(hubtoolsStr, "true"); // Check for allowOverwrite metadata from JavaScript (for hub.txt overwrites) char *allowOverwriteStr = jsonQueryString(req, "", "Event.Upload.MetaData.allowOverwrite", NULL); boolean allowOverwrite = (allowOverwriteStr && (sameString(allowOverwriteStr, "TRUE") || sameString(allowOverwriteStr, "true"))); boolean forceOverwrite = isHubToolsUpload || allowOverwrite; long currQuota = checkUserQuota(userName); long newQuota = currQuota + reqFileSize; long maxQuota = getMaxUserQuota(userName); @@ -130,31 +129,33 @@ char *reqFileType = jsonQueryString(req, "", "Event.Upload.MetaData.fileType", NULL); if (!isFileTypeRecognized(reqFileType)) { errAbort("File type '%s' for file '%s' is not accepted at this time", reqFileType, reqFileName); } char *reqGenome = jsonQueryString(req, "", "Event.Upload.MetaData.genome", NULL); if (!reqGenome) { errAbort("Genome selection is NULL for file '%s' is invalid. Please choose the correct genome", reqFileName); } // Block 2bit uploads whose genome name collides with a UCSC native database or GenArk hub. if (sameOk(reqFileType, "2bit") && reqGenome[0] && (hDbExists(reqGenome) || isGenArk(reqGenome))) { - char *hubName = reqParentDir ? hubNameFromPath(reqParentDir) : ""; + // existingHubTypeForDir looks up the hub's row at the top level, so give it + // the hub component of parentDir rather than a nested subdirectory + char *hubName = hubRootFromParentDir(reqParentDir); char *existingHubType = existingHubTypeForDir(userName, hubName); if (!sameOk(existingHubType, "assemblyHub")) errAbort(HUB_GENOME_COLLISION_ERR_FMT, reqGenome, reqGenome); } // we've passed all the checks so we can return that we are good to upload the file if (exitStatus == 0) { // set the location of the upload to the location it will ultimately live char *location = setUploadPath(userName, reqFileName, reqParentDir, forceOverwrite); if (!location) { errAbort("Error setting upload path in pre-create for file '%s'. This is an" " issue with our server, please email genome-www@soe.ucsc.edu with your" " userName so we can investigate.", reqFileName); @@ -168,31 +169,31 @@ jsonObjectAdd(changeObj, "Storage", pathObj); jsonObjectAdd(changeObj, "ID", newJsonString(makeRandomKey(128))); jsonObjectAdd(response, "ChangeFileInfo", changeObj); fillOutHttpResponseSuccess(response); } } // pop the handlers before handling the error, so an errAbort in the error // path cannot longjmp back into this same block errCatchEnd(errCatch); if (errCatch->gotError) { // App-level reject: tusd treats exit 0 + RejectUpload=true as a clean // rejection and forwards our HTTPResponse body verbatim. Non-zero // would be wrapped in "ERR_INTERNAL_SERVER_ERROR ... from hook // endpoint: ..." which buries the real message. - rejectUpload(response, errCatch->message->string); + rejectUpload(response, "%s", errCatch->message->string); exitStatus = 0; } } // always print a response no matter what jsonPrintToFile(response, NULL, stdout, 0); return 0; } int main(int argc, char *argv[]) /* Process command line. */ { optionInit(&argc, argv, options); if (argc != 1) usage(); return preCreate();