16

I've been Google searching this I may be having some brain clouds because it just isn't working.

I need to detect if a folder is a junction so my recursive file search doesn't run off into an endless loop.

I could use a simple function like

IsJunction(attr: dword): boolean; 

where attr is dwFileAttributes from TWin32FindData;

I just can't seem to get it to work. Thanks!

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Daniel
  • 315
  • 5
  • 16
  • 3
    I rolled back the question. Your edit completely changed the question. The question you asked has been answered expertly by Sertac. The next step for you is to accept that answer. If you cannot make your find function work, that can be the subject of another question. Please, one question at a time, and don't move the goal posts. – David Heffernan Nov 14 '12 at 20:29
  • 1
    I disagree and don't see it as a different question but whatever. Thanks. – Daniel Nov 14 '12 at 20:30
  • 7
    It's obviously a different question. Sertac gave you a functioning `IsJunction`. Suppose someone had then answered fixing the bug in your search code. Now, which of the two answers would you accept? Because a question can only have one accepted answer, it follows that questions can only ask a single question. At least, that's my take from having answered thousands of questions!! ;-) – David Heffernan Nov 14 '12 at 20:34

2 Answers2

12

dwFileAttributes of TWin32FindData does not have that information, you have to look to the dwReserved0 field. See documentation.

function IsJunction(const FileName: string): Boolean;
//  IO_REPARSE_TAG_MOUNT_POINT = $A0000003;
var
  FindHandle: THandle;
  FindData: TWin32FindData;
begin
  Result := False;
  FindHandle := FindFirstFile(PChar(FileName), FindData);
  if FindHandle <> INVALID_HANDLE_VALUE then begin
    Result := (Bool(FindData.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT))
              and Bool(FindData.dwReserved0 and $80000000) // MS bit
              and Bool(FindData.dwReserved0 and $20000000) // name surrogate bit
              and (LoWord(FindData.dwReserved0) = 3); // mount point value
    winapi.windows.FindClose(FindHandle);
  end else
    RaiseLastOSError;
end;
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • The `dwFileAttributes` field includes the `FILE_ATTRIBUTE_REPARSE_POINT` flag (the doc you linked to even says so). You only need to look at the `dwReserved0` field if you want to access the actual reparse point information, such as the reparse point tag value. – Remy Lebeau Nov 14 '12 at 18:33
  • @Remy, yes. I actually commented the same thing to David's answer and then upvoted his answer when he added the latter function. But the question indeed seems to be actually asking to identify a junction point. – Sertac Akyuz Nov 14 '12 at 18:36
  • +1 Very good. I simply had not realised that you can skip the DeviceIoControl call if all you need is the reparse tag. This is a much better answer than mine. – David Heffernan Nov 14 '12 at 19:59
  • I am still getting results like: C:\ProgramData\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Application Data\Common Files\ with that function where it should be skipping the folder completely as it is a junction. – Daniel Nov 14 '12 at 20:05
  • @Daniel - Then you've some other error in your code. I tested both this post and David's answer to return true for IsJunction for 'C:\ProgramData\Application Data'. – Sertac Akyuz Nov 14 '12 at 20:06
  • 2
    Indeed. When you are trying to debug a problem, don't pick a complex test environment. Pick the simplest environment possible. A simple call to `IsJunction`. Does it give the right answer or not? If it does, then add it in to your program. If your program then fails, you know to look at your program rather than `IsJunction`. – David Heffernan Nov 14 '12 at 20:13
  • I updated my question with the full code and how it is implemented. – Daniel Nov 14 '12 at 20:24
  • @David - Your answer was quite fine IMHO. First, it was not wrong, and second, it included a valuable suggestion about other reparse points. – Sertac Akyuz Nov 14 '12 at 21:00
  • @Sertac I know, and thanks. Yours is just better. It's the right answer to the question. I think this is a good question and it deserves your answer not to be diluted. – David Heffernan Nov 14 '12 at 21:03
  • @Daniel - alternatively, after the 'if dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY > 0 then begin' line put 'if not IsJunction(FFindData) then' and remove the first IsJunction. (I removed my other suggestion/comment, this is better I think.) – Sertac Akyuz Nov 14 '12 at 21:30
7

You can try also JCL (JEDI Code Library) JclNTFS unit.
it has a few methods to deal with junctions e.g:
NtfsIsFolderMountPoint / NtfsGetJunctionPointDestination.

kobik
  • 21,001
  • 4
  • 61
  • 121