Announcement

Collapse
No announcement yet.

Folder.DeleteTree in a symbolic link path

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Folder.DeleteTree in a symbolic link path

    Hi.
    The issue I am having is that the Folder.DeleteTree action seems not to work in case of symbolic link paths. Even worse - it seems to "de-link" the whole path. Is there any way to troubleshoot this kind of issue?


    Code:
    -->> Warning message if a certain file of v1.0 is found:
    -->> 1st file test
    result = Folder.DoesExist("%AppFolder%" .. "\\folder2"); --check if this file exists at a certain path to check if the scenery installed
    if result == true then
    repeat
    DialogResult = Dialog.Message("Previous version of "..productname.." is installed!", DialogLines, MB_YESNO, MB_ICONQUESTION, MB_DEFBUTTON1); --warning message about confirmed existence of the previous version
    if (DialogResult == IDNO) then
    Dialog.Message("Manual removal", DialogFolders, MB_OK, MB_ICONEXCLAMATION);
    else
    
    Folder.DeleteTree("%AppFolder%" .. "\\folder1", nil);
    Folder.DeleteTree("%AppFolder%" .. "\\folder2", nil);
    It seems that the Folder.DeleteTree does not remove the actual folder but instead de-links this folder and all folders at all levels above, in the folder tree.
    Thank for any help

  • #2
    I've done some tests and here is how it works.

    Let's say that:
    C:\Users\User\Desktop\TEST Actual\Bottom Level\Install Level\Folder1
    ...is "connected" (linked) to the...
    E:\TEST Actual\Bottom Level\Install Level\Folder1
    ...folder. This means that C:\ path is the soft link and E:\ path is the real data.

    The installer does not know that the C:\ location is not real, but there are no problems during the install.
    However when the Folder.DeleteTree is used to remove the C:\Users\User\Desktop\TEST Actual\Bottom Level\Install Level\Folder1 folder, what happens is:

    1. Not only the Folder1 folder is removed from C:\ but the entire folder tree up to C:\Users\User\Desktop (the whole symbolic link structure)
    2. The "real" data stored on E:\ stays untouched.

    Again - during install everything is perfect, the installer does not seem to care if that C:\ is a real location. The problem is only with the Folder.DeleteTree command.

    Comment


    • #3
      I am not sure what you expected? I do not see an error here, this is how links work.

      Symbolic Link
      A symbolic link contains a text string that is automatically interpreted and followed by the operating system as a path to another file or directory. This other file or directory is called the "target". The symbolic link is a second file that exists independently of its target. If a symbolic link is deleted, its target remains unaffected.
      If you delete a symbolic link, the files/folders it points to become indeed disconnected, but they continue to exist. In order to delete everything, you need to first delete the file/folder on its original paths, and then delete the symbolic link itself.

      Ulrich

      Comment


      • #4
        So basically there is no way to remove a folder which is "connected" via the symbolic link, not knowing if this is actually a symbolic link or not?

        Comment


        • #5
          Well, I mean that in this case not only the Folder1 is deleted from here:
          C:\Users\User\Desktop\TEST Actual\Bottom Level\Install Level\Folder1
          but the entire folder tree, with everything inside it, up to the desktop folder. I this an expected behavior?

          And is there any other way to remove the actual folder and the link to it?

          Comment


          • #6
            Yes, what you see is expected. When you use rmdir /s on the command line, in the same manner, the folder and subfolders are deleted, and symbolic links are deleted without actually removing the target to which the symbolic link was pointing to. It works in the same manner as shortcuts, where you would delete a folder containing a shortcut, but not the target of the shortcut. If this is what you need, you will need to build an extension, most likely calling this API function.

            Most likely, you could use File.Find() and list all the files and folders, call the function recursively for any folder found, and delete every file individually, before removing the folder itself once it is empty. I suspect that this will will bring the desired result, but it will take a much longer time to process than requesting the deletion of the folder tree directly.

            Ulrich

            Comment


            • #7
              Ulrich, thank you so much for the explanation. Let me double-check this, please.

              "Linked" folder (bolded is the symbolic link structure):
              C:\somedata\FirstLinkedFolder\something\Folder1

              Actual data:
              H:\FirstLinkedFolder\something\Folder1

              My %AppFolder% points to:
              C:\somedata\FirstLinkedFolder\something


              Basically, by using
              Folder.DeleteTree("%AppFolder%" .. "\\folder1", nil);
              ...I would expect the...
              C:\somedata\FirstLinkedFolder\something\Folder1
              ...to be deleted or the link to it removed (just as it would be with a shortcut). But instead the whole symbolic link structure is removed, with other files/folders inside it, at "higher" levels than %AppFolder%, basically all the way up:
              C:\somedata\FirstLinkedFolder\something\Folder1
              The whole underlined directory disappears, not only the Folder1 folder in %AppFolder%.

              Please confirm that this is expected and the only method to correctly remove Folder1 would be to remove all the files inside it first and then use f/e Folder.Delete .

              Comment


              • #8
                No, what you describe in your last message is not what I would expect, and also is not what I see actually happening, no matter if I perform this task using the rmdir command at the prompt, or with Folder.DeleteTree(). Here is what I did.

                I created a folder structure as shown, and copied a few documents into the "To be deleted" sub folder.

                Click image for larger version

Name:	SCRN-2020-09-29-01.png
Views:	195
Size:	1.8 KB
ID:	305546

                As you can see, the "Linked" folder is actually a symbolic link to another path.

                I deleted the sub folder with the command prompt.

                Click image for larger version

Name:	SCRN-2020-09-29-03.png
Views:	111
Size:	3.6 KB
ID:	305547

                There were no issues. Only the desired sub folder was deleted, the symbolic link was kept in place, as were the two sub folders.

                Click image for larger version

Name:	SCRN-2020-09-29-04.png
Views:	109
Size:	1.8 KB
ID:	305548

                After recreating the "To be deleted" sub folder, now I repeated the same using scripting.

                Click image for larger version

Name:	SCRN-2020-09-29-05.png
Views:	113
Size:	2.0 KB
ID:	305549

                When this code was executed, I had the exact same results as performing the command at the prompt - only the desired folder was deleted and everything else remained in place. I attached a short video of this happening.

                Ulrich

                Comment


                • #9
                  Hi.
                  OK, the way you described it is exactly the way I would expect it to work, however, in the case of my installer, a user showed to me that issue on his setup and I was able to recreate it on mine. In my case, this is fully repeatable. If it helps I can shoot a video of that.

                  This might not matter but the symbolic link was created using the following type of command:
                  mklink /J "C:\Users\user\Desktop\TestVirtual" "E:\TEST Actual"

                  The Folder1 folder was then placed inside the following directory:
                  C:\Users\user\Desktop\TestVirtual\testfolder\mainfolder\community\

                  In my test environment, the action to do was:
                  Folder.DeleteTree("%AppFolder%" .. "\\folder1", nil);
                  where %AppFolder% was C:\Users\user\Desktop\TestVirtual\testfolder\mainfolder\community\

                  ...and this action lead to the removal of the whole C:\Users\user\Desktop\TestVirtual folder instead.

                  Comment


                  • #10
                    Here is the video:




                    and here is the whole code from the Before Installing -> Select Packages section.


                    Code:
                     -- Locate Appdata/Local
                    AppDataLocal = Shell.GetFolder(SHF_APPLICATIONDATA_LOCAL);
                    SessionVar.Set("%AppDataLocal%", AppDataLocal);
                    -- Check if UserCfg.opt is there
                    result = File.DoesExist("%AppDataLocal%" .. "\\Packages\\Microsoft.FlightSimulator_8wekyb3d8bb we\\LocalCache\\UserCfg.opt");
                    if result == true then
                    
                    SessionVar.Set("%usercfg%", "%AppDataLocal%" .. "\\Packages\\Microsoft.FlightSimulator_8wekyb3d8bb we\\LocalCache\\UserCfg.opt");
                    -- read UserCfg.opt
                    mytable = TextFile.ReadToTable("%usercfg%");
                    -- read the file into a string
                    local sConfig = TextFile.ReadToString(SessionVar.Expand("%usercfg% "));
                    -- find what matters
                    local sPath = string.match(sConfig, ".*InstalledPackagesPath \"(.*)\".*");
                    FS2020_path = tostring(sPath)
                    
                    
                    SessionVar.Set("%FS2020_path%", FS2020_path);
                    SessionVar.Set("%AppFolder%", FS2020_path .. "\\Community");
                    SessionVar.Set("%installoption%", "MS Store (UserCfg.opt used)");
                    end
                    
                    
                    --------------------------------------------
                    
                    
                    -- Check if UserCfg.opt is there
                    result = File.DoesExist("%ApplicationDataFolder%" .. "\\Microsoft Flight Simulator\\UserCfg.opt");
                    if result == true then
                    
                    SessionVar.Set("%usercfg%", "%ApplicationDataFolder%" .. "\\Microsoft Flight Simulator\\UserCfg.opt");
                    -- read UserCfg.opt
                    mytable = TextFile.ReadToTable("%usercfg%");
                    -- read the file into a string
                    local sConfig = TextFile.ReadToString(SessionVar.Expand("%usercfg% "));
                    -- find what matters
                    local sPath = string.match(sConfig, ".*InstalledPackagesPath \"(.*)\".*");
                    FS2020_path = tostring(sPath)
                    
                    SessionVar.Set("%FS2020_path%", FS2020_path);
                    SessionVar.Set("%AppFolder%", FS2020_path .. "\\Community");
                    SessionVar.Set("%installoption%", "Steam (UserCfg.opt used)");
                    
                    end
                    
                    --------------------------------------------
                    
                    -- if file is not found:
                    result = Folder.DoesExist("%FS2020_path%" .. "\\Community");
                    if result == false then
                    
                    
                    -- try getting it from registry
                    FS2020_path = Registry.GetValue(HKEY_CURRENT_USER, "Software\\Drzewiecki Design\\FS2020", "Path");
                    SessionVar.Set("%installoption%", "Registry entry used");
                    end
                    
                    
                    --------------------------------------------
                    
                    --if nothing works request manual entry
                    
                    result = Folder.DoesExist(FS2020_path .. "\\Community");
                    if result == false then
                    
                    
                    repeat
                    Dialog.Message("Microsoft Flight Simulator folder not found", "Microsoft Flight Simulator folder directory on this computer is unknown. Please select your main Microsoft Flight Simulator folder (the one where Community and Official folders are located) in the next window. This is required at the first install only.", MB_OK, MB_ICONEXCLAMATION, MB_DEFBUTTON1);
                    FS2020_path = Dialog.FolderBrowse("Please select your main Microsoft Flight Simulator folder (Community and Official folders should be inside it)", "");
                    -- If CANCEL is pressed, exit the installer
                    if (FS2020_path == "CANCEL") then
                    Dialog.Message("Installation Aborted", "You need to select your Microsoft Flight Simulator folder else this installer cannot continue!", MB_OK, MB_ICONSTOP);
                    Application.Exit(1)
                    end
                    -- If CANCEL was not chosen, then let's get the folder path
                    if (FS2020_path ~= "CANCEL") then
                    result = Folder.DoesExist(FS2020_path .. "\\Community");
                    end
                    until result == true
                    -- Add FS2020 path to Drzewiecki Design registry key to avoid asking for folder directory in future installs
                    Registry.SetValue(HKEY_CURRENT_USER, "SOFTWARE\\Drzewiecki Design\\FS2020", "Path", FS2020_path, REG_SZ);
                    Dialog.Message("Microsoft Flight Simulator folder", "Microsoft Flight Simulator folder directory has been successfully saved. Click OK to proceed.", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
                    SessionVar.Set("%installoption%", "First time manual install - registry entry created");
                    end
                    
                    
                    SessionVar.Set("%FS2020_path%", FS2020_path);
                    SessionVar.Set("%AppFolder%", FS2020_path .. "\\Community");
                    
                    
                    
                    
                    --------------------------------------------
                    --------------------------------------------
                    
                    
                    -->> Check if the PREVIOUS VERSION of the scenery is installed and if so, remove it
                    productname = "Moscow Landmarks MSFS"; --name of the product to remove !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!
                    appfolderpath = SessionVar.Expand("%AppFolder%"); --path of scenery folders to delete
                    foldersdel = "Please delete the following folders:\n\ndrzewieckidesign-moscowlandmarks\ndrzewieckidesign-moscowliteairports\n\n...located in: \n"..appfolderpath..""
                    
                    -->> Warning message
                    DialogLines = "Previous version of "..productname.." is installed! You need to remove the previous version of "..productname.." else this installer cannot continue.\n\nWould you like our installer to do that automatically?";
                    -->>Dialog with folder specs
                    DialogFolders = "You have selected to manually remove the previous version of "..productname.."\n\n"..foldersdel;
                    
                    -->> Warning message if a certain file of v1.0 is found:
                    -->> 1st file test
                    result = Folder.DoesExist("%AppFolder%" .. "\\drzewieckidesign-moscowliteairports"); --check if this file exists at a certain path to check if the scenery installed
                    if result == true then
                    repeat
                    DialogResult = Dialog.Message("Previous version of "..productname.." is installed!", DialogLines, MB_YESNO, MB_ICONQUESTION, MB_DEFBUTTON1); --warning message about confirmed existence of the previous version
                    if (DialogResult == IDNO) then
                    Dialog.Message("Manual removal", DialogFolders, MB_OK, MB_ICONEXCLAMATION);
                    else
                    -->> FOLDERS AND FILES TO DELETE
                    --> Community Folder directory (FOLDER):
                    Folder.DeleteTree("%AppFolder%" .. "\\drzewieckidesign-moscowlandmarks", nil);
                    Folder.DeleteTree("%AppFolder%" .. "\\drzewieckidesign-moscowliteairports", nil);
                    --File.Delete("%AppFolder%" .. "\\drzewieckidesign-moscowliteairports\\scenery\\world\\scenery\\mosco wairportslite.bgl", false, false, false, nil);
                    
                    -------
                    end
                    -->> 2nd file test
                    result = File.DoesExist("%AppFolder%" .. "\\drzewieckidesign-moscowliteairports\\scenery\\world\\scenery\\mosco wairportslite.bgl");
                    until result == false
                    Dialog.Message("Product removal", "The procedure has been completed succesfully. Please proceed with the install.", MB_OK, MB_ICONINFORMATION);
                    end
                    
                    
                    --------------------------------------------
                    --------------------------------------------
                    
                    
                    -- These actions are performed before the screen is shown.
                    
                    -- calculate the amount of space required for the installation
                    _SpaceRequired = SetupData.CalculateRequiredSpace();
                    
                    -- format it as a string with an appropriate unit of measurement (e.g. "0 bytes")
                    local strSpaceRequired = String.GetFormattedSize(_SpaceRequired);
                    
                    -- store the string in a session variable so it can be used in the screen text
                    SessionVar.Set("%SpaceRequired%", strSpaceRequired);
                    
                    -- from _SUF70_Global_Functions.lua:
                    -- update the 'Total space required:' message (expands any session variables in it)
                    g_UpdateStaticTextCtrl(CTRL_STATICTEXT_SPACEREQUIR ED, "IDS_CTRL_STATICTEXT_SPACEREQUIRED");

                    Comment


                    • #11
                      Originally posted by Stanislaw View Post
                      In my test environment, the action to do was:
                      Folder.DeleteTree("%AppFolder%" .. "\\folder1", nil);
                      where %AppFolder% was C:\Users\user\Desktop\TestVirtual\testfolder\mainfolder\community\
                      Without expanding the session variable properly?? This could explain your problems.

                      Code:
                      [I]Folder.DeleteTree(SessionVar.Expand("%AppFolder%\\[B]folder1[/B]"), nil);[/I]
                      Also, your command shows that you are using a junction and not a symbolic link, as you mentioned until now. There might be differences.

                      There are multiple errors in this code you posted.

                      Click image for larger version  Name:	SCRN-2020-09-29-01.png Views:	1 Size:	15.3 KB ID:	305554

                      Ulrich

                      Comment


                      • #12
                        Well, it works perfectly in a normal environment, so I haven't even considered it...
                        So would this make it any different?

                        Folder.DeleteTree(SessionVar.Expand("%AppFolder%") .. "\\folder1", nil);

                        No idea about the "junctions" really... :(

                        Comment


                        • #13
                          With the expanded variable the result is the same.

                          Comment


                          • #14
                            As long as you expand the session variable properly, you can concatenate any fixed strings as you prefer. Both lines will yield the same result, and I prefer the shorter version.
                            Code:
                            [I]Folder.DeleteTree(SessionVar.Expand("%AppFolder%") .. "\\[B]folder1[/B]", nil);
                            Folder.DeleteTree(SessionVar.Expand("%AppFolder%\\[B]folder1[/B]"), nil);[/I]
                            Ulrich

                            Comment


                            • #15
                              With

                              Folder.DeleteTree(SessionVar.Expand("%AppFolder%") .. "\\folder1", nil);

                              the result is the same, unfortunately. The whole linked folder disappears...

                              Comment

                              Working...
                              X