2c8a873e7286d797e88549d5a2825f0ea6fe3f04 chmalee Fri May 15 15:07:52 2026 -0700 HubSpace now allows tracks hubs to be defined via hub.txt+genomes.txt+trackDb.txt files, refs Baihe email and #37566 diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c index 9c36f9f7d12..2cb2abf7f61 100644 --- src/hg/lib/userdata.c +++ src/hg/lib/userdata.c @@ -336,31 +336,32 @@ if (sameString(subdir, ".")) continue; if (!subdir) errAbort("error: empty subdirectory components for parentDir string '%s'", parentDirStr); if (lastChar(dyStringContents(currLocation)) != '/') dyStringAppendC(currLocation, '/'); dyStringAppend(currLocation, subdir); struct hubSpace *row = NULL; AllocVar(row); row->userName = userName; row->fileName = subdir; row->fileSize = 0; row->fileType = "dir"; row->creationTime = NULL; row->lastModified = sqlUnixTimeToDate(&lastModified, TRUE); - row->db = db; + // Leaf-only db; ancestors sit above per-genome subdirs. + row->db = (i == foundSlashes - 1) ? db : ""; row->location = cloneString(dyStringContents(currLocation)); row->md5sum = ""; row->parentDir = i > 0 ? components[i-1] : ""; row->hubType = hubType ? hubType : "trackHub"; // only insert a row for this parentDir if it's unique to the table if (!checkHubSpaceRowExists(row)) addHubSpaceRowForFile(row); } } static char *defaultPosFromTwoBit(char *twoBitPath) /* Open the 2bit, pick the first sequence and return "chrom:1-min(size,1000)". * Returns NULL if the 2bit cannot be opened, has no sequences, or the first * sequence name would inject content into hub.txt. */ { @@ -738,64 +739,72 @@ hubTextRow->hubType = rowForFile->hubType ? rowForFile->hubType : "trackHub"; if (!checkHubSpaceRowExists(hubTextRow)) addHubSpaceRowForFile(hubTextRow); } static void deleteHubSpaceRow(char *fname, char *userName) /* Deletes a row from the hubspace table for a given fname */ { struct sqlConnection *conn = hConnectCentral(); struct dyString *deleteQuery = sqlDyStringCreate("delete from hubSpace where location='%s' and userName='%s'", fname, userName); sqlUpdate(conn, dyStringCannibalize(&deleteQuery)); hDisconnectCentral(&conn); } void removeFileForUser(char *fname, char *userName) -/* Remove a file for this user if it exists */ +/* Remove a file (or recursively, a directory) for this user if it exists */ { // The file to remove must be prefixed by the hg.conf userDataDir char canonicalPath[PATH_MAX]; -realpath(fname, canonicalPath); +if (realpath(fname, canonicalPath) == NULL) + return; if (!startsWith(getDataDir(userName), canonicalPath)) return; -if (fileExists(canonicalPath)) - { - // If removing a hub directory, clean up the per-hub flock file so - // rmdir doesn't fail with ENOTEMPTY. The .hub.lock file is a backend - // artifact, never recorded as a hubSpace row. +if (!fileExists(canonicalPath)) + return; + if (isDirectory(canonicalPath)) { + // Clean up the per-hub flock file (a backend artifact, not in hubSpace). struct dyString *lockPath = dyStringCreate("%s%s.hub.lock", canonicalPath, endsWith(canonicalPath, "/") ? "" : "/"); if (fileExists(dyStringContents(lockPath))) - unlink(dyStringContents(lockPath)); + mustRemove(dyStringContents(lockPath)); dyStringFree(&lockPath); + + // Recurse into children so rmdir succeeds; listDirX("*") skips + // dotfiles, and .hub.lock was already removed above. + struct fileInfo *entries = listDirX(canonicalPath, "*", TRUE); + struct fileInfo *e; + for (e = entries; e != NULL; e = e->next) + removeFileForUser(e->name, userName); + slFreeList(&entries); } - // delete the actual file + +// delete the file (or now-empty directory) mustRemove(canonicalPath); // delete the table row, which probably has the location based // on the other filesystem if (checkHubSpaceLocationExists(userName, canonicalPath)) deleteHubSpaceRow(canonicalPath, userName); else { char *unswapped = unswapDataDir(userName, canonicalPath); if (checkHubSpaceLocationExists(userName, unswapped)) deleteHubSpaceRow(unswapped, userName); } - } // TODO: we should also modify the hub.txt associated with this file } struct hubSpace *listFilesForUser(char *userName) /* Return the files the user has uploaded */ { struct sqlConnection *conn = hConnectCentral(); struct dyString *query = sqlDyStringCreate("select userName, fileName, fileSize, fileType, creationTime, DATE_FORMAT(lastModified, '%%c/%%d/%%Y, %%l:%%i:%%s %%p') as lastModified, db, location, md5sum, parentDir, hubType from hubSpace where userName='%s' order by location,creationTime", userName); struct hubSpace *fileList = hubSpaceLoadByQuery(conn, dyStringCannibalize(&query)); hDisconnectCentral(&conn); return fileList; } #define defaultHubName "defaultHub" char *defaultHubNameForUser(char *userName)