aa0273b99929f6d4c9d4a46589d2c9c9b72b98b5 chmalee Tue Sep 23 10:29:45 2025 -0700 Fix hubspace deletion when files are on a different filesystem: when deleting the row from the hgcentral table, we look up the filename by a string that needs the original filesystem path, so one, for safety check that the location we are trying to delete exists before issuing the delete command, and two, unswap the dataDir prefix and delete based off of that if the swapped location is not in the table, refs #31058 diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c index a951b1c60a2..eeb7b3e32f8 100644 --- src/hg/lib/userdata.c +++ src/hg/lib/userdata.c @@ -110,30 +110,45 @@ * 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 *ret = cloneString(in); char *tusdDataDir = cfgOption("tusdDataDir"); char *tusdMountPoint = cfgOption("tusdMountPoint"); if (tusdMountPoint && !isEmpty(tusdMountPoint)) { ret = replaceChars(in, tusdDataDir, tusdMountPoint); } return ret; } +char *unswapDataDir(char *userName, char *in) +/* Opposite of swapDataDir, for trusting that the other system string is + * correct. Used for deleting the row in the table for a file which has + * tusdDataDir as the prefix, not the tusdMountPoint. */ +{ +char *ret = cloneString(in); +char *tusdDataDir = cfgOption("tusdDataDir"); +char *tusdMountPoint = cfgOption("tusdMountPoint"); +if (tusdMountPoint && !isEmpty(tusdMountPoint)) + { + ret = replaceChars(in, tusdMountPoint, tusdDataDir); + } +return ret; +} + char *stripDataDir(char *fname, char *userName) /* Strips the getDataDir(userName) off of fname. The dataDir may be a symbolic * link, or on a different filesystem. */ { getDataDir(userName); if (!dataDir) { // catch a realpath error return NULL; } int prefixSize = strlen(dataDir); if (startsWith(dataDir, fname)) { char *ret = fname + prefixSize; return ret; @@ -214,30 +229,40 @@ return cloneString(canonicalPath); } return NULL; } static boolean checkHubSpaceRowExists(struct hubSpace *row) /* Return TRUE if row already exists */ { struct sqlConnection *conn = hConnectCentral(); struct dyString *queryCheck = sqlDyStringCreate("select count(*) from hubSpace where userName='%s' and fileName='%s' and parentDir='%s'", row->userName, row->fileName, row->parentDir); int ret = sqlQuickNum(conn, dyStringCannibalize(&queryCheck)); hDisconnectCentral(&conn); return ret > 0; } +static boolean checkHubSpaceLocationExists(char *userName, char *location) +/* Return TRUE if location exists for userName and has exactly one row */ +{ +struct sqlConnection *conn = hConnectCentral(); +struct dyString *queryCheck = sqlDyStringCreate("select count(*) from hubSpace where userName='%s' and location='%s'", userName, location); +int ret = sqlQuickNum(conn, dyStringCannibalize(&queryCheck)); +hDisconnectCentral(&conn); +return ret == 1; +} + char *hubNameFromPath(char *path) /* Return the last directory component of path. Assume that a '.' char in the last component * means that component is a filename and go back further */ { char *copy = cloneString(path); if (endsWith(copy, "/")) trimLastChar(copy); char *ptr = strrchr(copy, '/'); // check to see if we're in a file name, like /blah/blah/name/hub.txt if (ptr) { if (strchr(ptr, '.')) { *ptr = 0; ptr = strrchr(copy, '/'); @@ -437,32 +462,41 @@ hDisconnectCentral(&conn); } void removeFileForUser(char *fname, char *userName) /* Remove a file 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 (!startsWith(getDataDir(userName), canonicalPath)) return; if (fileExists(canonicalPath)) { // delete the actual file mustRemove(canonicalPath); - // delete the table row + + // 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(); + 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 from hubSpace where userName='%s' order by location,creationTime", userName); struct hubSpace *fileList = hubSpaceLoadByQuery(conn, dyStringCannibalize(&query)); hDisconnectCentral(&conn); return fileList; } #define defaultHubName "defaultHub"