544b4db19ee0f7f92c9d338d6900c34a53d9f032
chmalee
  Wed Jun 11 15:45:15 2025 -0700
Add back the good parts of b318572799b35, mostly have the pre-create hook set the upload path so we don't have to move files around in the pre-finish hook

diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c
index 3823add7dac..0cca26e42c8 100644
--- src/hg/lib/userdata.c
+++ src/hg/lib/userdata.c
@@ -27,64 +27,93 @@
 {
 return (loginSystemEnabled() || wikiLinkEnabled()) ? wikiLinkUserName() : NULL;
 }
 
 char *emailForUserName(char *userName)
 /* Fetch the email for this user from gbMembers hgcentral table */
 {
 struct sqlConnection *sc = hConnectCentral();
 struct dyString *query = sqlDyStringCreate("select email from gbMembers where userName = '%s'", userName);
 char *email = sqlQuickString(sc, dyStringCannibalize(&query));
 hDisconnectCentral(&sc);
 // this should be freeMem'd:
 return email;
 }
 
-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 */
+char *getEncodedUserNamePath(char *userName)
+/* Compute the path for just the userName part of the users upload */
+{
+struct dyString *ret = dyStringNew(0);
+if (!userName)
+    return NULL;
+char *encUserName = cgiEncode(userName);
+char *userPrefix = md5HexForString(encUserName);
+userPrefix[2] = '\0';
+dyStringPrintf(ret, "%s/%s", userPrefix, encUserName);
+return dyStringCannibalize(&ret);
+}
+
+// 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 '/')");
 
 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
 // so the rest of the routines can append to this result
 
 dyStringClear(newDataDir);
 dyStringPrintf(newDataDir, "%s/", canonicalPath);
-return dyStringCannibalize(&newDataDir);
+
+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);
+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() */
 {
-char *dataDir = getDataDir(userName);
+getDataDir(userName);
 if (!dataDir)
     {
     // catch a realpath error
     return NULL;
     }
 int prefixSize = strlen(dataDir);
 if (startsWith(dataDir, fname))
     {
     char *ret = fname + prefixSize;
     return ret;
     }
 return NULL;
 }
 
 char *getHubDataDir(char *userName, char *hub)