11d5b26d9798079ec5adedc103216ce818e2da5e max Thu Sep 10 05:19:27 2026 -0700 Widen the session data directory hash from 8 to 10 hex characters sessionDirFromNames() named a session's durable data directory with 8 hex characters of md5(sessionName). 32 bits was fine while a directory only had to be unique among one user's sessions, but every anonymous "Share a link" session belongs to the single reserved user "l", which makes it a birthday problem across all of them: two unrelated sessions land in the same directory more likely than not at around 77,000 anonymous sessions, and at 500,000 we would expect about 29 such pairs. Two sessions sharing a directory means cleaning up one takes the other's custom track files with it. sessionDirHashLen is now 10 (40 bits), which moves the even-odds point past a million sessions. The two fan-out levels snapshotSessionDir() added for user "l" do not help here, since they are a prefix of the same hash: they spread the entries over 65536 directories but leave the number of distinct leaf names unchanged. Both directory layouts change name as a result, so snapshotCleaner would have walked past anything written earlier and orphaned its files. Both dir-naming functions grew a hashLen argument, sessionDirHashLenLegacy records the old value, and snapshotCleanAnon() now tries the old spelling of both layouts as well as the new one. Existing sessions keep working either way: the cart stores the absolute path of each durable file, so nothing looks a session's directory up by name except the cleaner. refs #10138 diff --git src/hg/inc/sessionData.h src/hg/inc/sessionData.h index 15129352ccd..7efe5a03c24 100644 --- src/hg/inc/sessionData.h +++ src/hg/inc/sessionData.h @@ -1,25 +1,41 @@ /* sessionData - functions for moving user data out of trash into permanent storage * * Copyright (C) 2019-2024 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #ifndef SESSIONDATA_H #define SESSIONDATA_H char *sessionDataSaveTrashFile(char *trashPath, char *sessionDir); /* If trashPath exists and is not already a soft-link to sessionDir, alloc and return a new path in * sessionDir; move trashPath to new path and soft-link from trashPath to new path. * If trashPath is already a soft-link, return the path that it links to. * Return NULL if trashPath does not exist (can happen with expired custom track files). */ +/* Number of hex characters of md5(encSessionName) used to name a session's data directory. + * This was 8 (32 bits) until 2026. That was safe while a directory only had to be unique among + * one user's sessions, but anonymous share links all live under the single reserved user name + * "l", which turns it into a birthday problem across every anonymous session: two of them share + * a directory more likely than not at ~77,000 sessions, and cleaning one up would take the + * other's files with it. 10 hex characters is 40 bits, which pushes that past 1,000,000. + * Directories written before the change are named with sessionDirHashLenLegacy characters, so + * code that deletes a session's directory must try both lengths. */ +#define sessionDirHashLen 10 +#define sessionDirHashLenLegacy 8 + char *sessionDirFromNames(char *sessionDataDir, char *encUserName, char *encSessionName); /* Alloc and return the per-session data directory under sessionDataDir (hashed by user and session * name), or NULL if sessionDataDir is empty. errAborts if sessionDataDir is not an absolute path. */ +char *sessionDirFromNamesHashLen(char *sessionDataDir, char *encUserName, char *encSessionName, + int hashLen); +/* Like sessionDirFromNames but with the number of session-hash characters spelled out, so that + * cleanup code can also name a directory written before sessionDirHashLen was widened. */ + void sessionDataSaveSession(struct cart *cart, char *encUserName, char *encSessionName, char *dbSuffix); /* If hg.conf specifies safe places to store files and/or tables that belong to user sessions, * then scan cart for trashDir files and/or customTrash tables, store them in safe locations, * and update cart to point to the new locations. */ #endif // SESSIONDATA_H