94b3342ec1b9abe3993347c967b652f2e91ecf57
angie
  Thu May 16 12:18:49 2019 -0700
When saving a trash path that is already a link, if it is a relative link (e.g. to ../../userdata/ct/xyz) then resolve it to a full path.  refs #22440

diff --git src/hg/hgSession/sessionData.c src/hg/hgSession/sessionData.c
index 6c339e3..6d0b764 100644
--- src/hg/hgSession/sessionData.c
+++ src/hg/hgSession/sessionData.c
@@ -63,41 +63,53 @@
     }
 }
 
 static void moveAndLink(char *oldPath, char *newPath)
 /* Make a hard link from newPath to oldPath; unlink oldPath; symlink oldPath to newPath. */
 {
 if (link(oldPath, newPath) != 0)
     errnoAbort("moveAndLink: link(oldPath='%s', newPath='%s') failed", oldPath, newPath);
 if (unlink(oldPath) != 0)
     errnoAbort("moveAndLink: unlink(oldPath='%s') failed", oldPath);
 if (symlink(newPath, oldPath) != 0)
     errnoAbort("moveAndLink: symlink(newPath='%s', oldPath='%s') failed", newPath, oldPath);
 }
 
 static char *saveTrashFile(char *trashPath, char *sessionDir)
-/* If trashPath exists and is not already a soft-link, alloc and return a new path in
+/* 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). */
 {
 char *newPath = NULL;
 if (fileExists(trashPath))
     {
     char *existingLink = maybeReadlink(trashPath);
     if (existingLink)
+        {
+        // It may be a multi-directory-level relative symlink created by the trashCleaner scripts
+        if (existingLink[0] != '/')
+            {
+            char trashPathDir[PATH_LEN];
+            splitPath(trashPath, trashPathDir, NULL, NULL);
+            char fullLinkPath[strlen(trashPathDir) + strlen(existingLink) + 1];
+            safef(fullLinkPath, sizeof fullLinkPath, "%s%s", trashPathDir, existingLink);
+            newPath = realpath(fullLinkPath, NULL);
+            }
+        else
             newPath = existingLink;
+        }
     else
         {
         newPath = sessionDataPathFromTrash(trashPath, sessionDir);
         if (fileExists(newPath))
             errAbort("saveTrashFile: new path '%s' already exists", newPath);
         makeDirsForFile(newPath);
         moveAndLink(trashPath, newPath);
         }
     }
 return newPath;
 }
 
 static char *nextTrashPath(char *string, char *trashDirPrefix)
 /* Alloc & return the next file path in string that starts with "../trash/", or NULL. */
 {