de181afb7e1d76c969ab0ec23c0b71de5aa7f1e4 chmalee Fri Jul 18 11:25:13 2025 -0700 Fix hubspace pre-create hook to check for an apiKey only if a valid login cookie was not provided. Add a new hg.conf statement that allows for the data directory to be on a separate mount from the CGI, and swap strings around in the path appropriately to verify file existence when the file exists on another server diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c index 0cca26e42c8..37416a52065 100644 --- src/hg/lib/userdata.c +++ src/hg/lib/userdata.c @@ -55,56 +55,76 @@ // make this a global so if we have to repeatedly call stripDataDir() // we only need to check the filesystem for path validity once static char *dataDir = NULL; static char *setDataDir(char *userName) /* Set the dataDir value based on hg.conf and the userName. Use realpath to make sure * the directory exists and resolve the path if it is a symlink. Return the final * path for convenience */ { char *tusdDataBaseDir = cfgOption("tusdDataDir"); if (!tusdDataBaseDir || isEmpty(tusdDataBaseDir)) errAbort("trying to save user file but no tusdDataDir defined in hg.conf"); if (tusdDataBaseDir[0] != '/') errAbort("config setting tusdDataDir must be an absolute path (starting with '/')"); +// the tusdDataBaseDir may be a symlink, so canonicalize it, but do not include +// the userName part since it may not exist yet: +char *canonicalPath = needMem(PATH_MAX); +realpath(tusdDataBaseDir, canonicalPath); + char *encUserName = cgiEncode(userName); char *userPrefix = md5HexForString(encUserName); userPrefix[2] = '\0'; -struct dyString *newDataDir = dyStringNew(0); -dyStringPrintf(newDataDir, "%s/%s/%s", - tusdDataBaseDir, userPrefix, encUserName); - -char *canonicalPath = needMem(PATH_MAX); -realpath(dyStringContents(newDataDir), canonicalPath); -// now that we have canonicalized the path we need to add a '/' back on +// now that we have a canonicalized the path we need to add a '/' back on // so the rest of the routines can append to this result - -dyStringClear(newDataDir); -dyStringPrintf(newDataDir, "%s/", canonicalPath); +struct dyString *newDataDir = dyStringNew(0); +dyStringPrintf(newDataDir, "%s/%s/%s/", + canonicalPath, userPrefix, encUserName); dataDir = dyStringCannibalize(&newDataDir); return dataDir; } char *getDataDir(char *userName) /* Return the full path to the user specific data directory, can be configured via hg.conf * on hgwdev, this is /data/tusd */ { if (!dataDir) setDataDir(userName); + +char *tusdMountPoint = cfgOption("tusdMountPoint"); +if (tusdMountPoint && !isEmpty(tusdMountPoint)) + { + // the data server may be somewhere else and mounted over NFS. In this + // case, when tusd saves files, it is writing it's local tusdDataDir + // value into the hgcentral file location. When the CGI running somewhere + // else needs to verify file existence, the tusdDataDir won't exist on the + // CGI filesystem, but will instead be mounted as some different path. + // In this case, replace tusdDataDir with tusdMountPoint + char *tusdDataDir = cfgOption("tusdDataDir"); + char *canonicalPath = needMem(PATH_MAX); + if (!startsWith(tusdDataDir, dataDir)) + { + // could be a symlink + realpath(tusdDataDir, canonicalPath); + } + else + canonicalPath = tusdDataDir; + strSwapStrs(dataDir, strlen(dataDir), canonicalPath, tusdMountPoint); + } return dataDir; } char *stripDataDir(char *fname, char *userName) /* Strips the getDataDir(userName) off of fname. The dataDir may be a symbolic * link, we will resolve it here. NOTE that this relies on * calling realpath(3) on the fname argument prior to calling stripDataDir() */ { getDataDir(userName); if (!dataDir) { // catch a realpath error return NULL; } int prefixSize = strlen(dataDir);