Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Source and destination must be different while using Path.Combine()

Tags:

c#

I am trying to create a batch charsheet creator for my RPG. When the program loads it looks in its file location WORKINGDIR + WAITDIR for any picture files that have the tags Ready, Male and Female tags and puts them in WORKINGDIR + LIVEDIR. WORKINGDIR looks like this:

Debug // WORKINGDIR
├───Live // LIVEDIR
├───Wait // WAITDIR
├───Crew.exe // program
└───Other VS files

When the code is run the error System.IO.IOException: 'Source and destination path must be different.' is thrown when I try to move the file from one to another. Somehow source and destination become equivalent.

const string LIVEDIR = "\\Live";
const string WAITDIR = "\\Wait";
List<string> files = new List<string>(Directory.EnumerateFiles(WORKINGDIR + WAITDIR, "*.jpeg", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.EnumerateFiles(WORKINGDIR + WAITDIR, "*.png", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.EnumerateFiles(WORKINGDIR + WAITDIR, "*.jpg", SearchOption.TopDirectoryOnly));
// ^ adds files that need to be looked through
string[] tags;
ShellFile shellFile;
foreach(string file in files)
{
    string source = Path.Combine(WORKINGDIR, WAITDIR, file);
    string destination = Path.Combine(WORKINGDIR, LIVEDIR, file);
    /*if (source == destination)
        throw new Exception();
    */ // This is also thrown
    shellFile = ShellFile.FromFilePath(Path.Combine(WORKINGDIR, WAITDIR, file));
    tags = shellFile.Properties.System.Photo.TagViewAggregate.Value;
    int i = 0;
    crewMember.Gender foo = crewMember.Gender.Female;
    if (tags == null)
        continue;
    foreach(string tag in tags)
    {
        if(tag == "Ready")
        {
            i++;
        }
        if(tag == "Female")
        {
            i++;
            foo = crewMember.Gender.Female;
        }
        else if(tag == "Male")
        {
            i++;
            foo = crewMember.Gender.Male;
        }
    }

    if(i>=2) // if it has two correct tags
    {
        Console.WriteLine(
            $"source:      {source}\n" +
            $"Destination: {destination}");
        // ^ also writes the same paths to console.
        Directory.Move(source, destination);
        // ^ Error
        crewMembers.Add(new crewMember(file, foo));
    }
}

What I think is going on is that for whatever reason Path.Combine() is returning the string that was generated previous instead of a new string. Is there a way around this or am I using it wrong?

like image 548
Doshorte Dovencio Avatar asked Jan 26 '23 13:01

Doshorte Dovencio


1 Answers

The method Directory.EnumerateFiles gives absolute paths, so each of your file variables contains an absolute path.

If you supply multiple absolute paths to Path.Combine, it will output the last one:

Path.Combine("C:\\", "workdir", "C:\\outdir\\test.txt")

yields

C:\outdir\test.txt

(Try it).

This is, because the Path.Combine method tries to root the output path:

However, if an argument other than the first contains a rooted path, any previous path components are ignored, and the returned string begins with that rooted path component.

Especially for applications which take file names as user input this is a common security pitfall.

Instead either use Path.GetFileName to get plain file names first, or, if you have access to .NET Core, combine the paths using the new and more secure Path.Join method.

like image 118
janw Avatar answered Jan 28 '23 15:01

janw