Modify

Opened 8 years ago

Closed 7 years ago

#2054 closed Bug (Rejected)

DirCreate incorrectly reports success when path is longer than 255 chars

Reported by: wraithdu Owned by:
Milestone: Component: AutoIt
Version: 3.3.7.21 Severity: None
Keywords: DirCreate Cc:

Description

DirCreate returns 1 (success) when the path is longer than 255 characters. However the path created is truncated to 255 characters, thus creating some unknown shortened path. This occurs even using correct unicode long path notation:

\\?\C:\some\really\long\path
or
\\?\UNC\server\share\some\really\long\path

DirCreate should either return 0 (failure) when a normal path is given that is longer that 255 characters, or it needs to actually create the correct extra long path when proper unicode long path notation is used.

See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx for reference.

Attachments (0)

Change History (8)

comment:1 Changed 8 years ago by Jpm

In fact the dir is created when using

\\?\

the only thing you can perhaps contest is FileExists or DirRemove does not found such path

Local $dir = "\\?\C:\some"
Local $subdir = "\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\long\path"

Local $len = StringLen($dir & $subdir)
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $len = ' & $len & @crlf & '>Error code: ' & @error & '    Extended code: 0x' & Hex( @extended) & @crlf) ;### Debug Console

Check_DirCreate($dir, $subdir)

Func Check_DirCreate($dir, $subdir)
	Local $ret = DirCreate($dir & $subdir)
	If $ret=0 Or @error Then
		MsgError("Dir Not created", $dir & $subdir, $ret, @error)
	Else
		$ret = FileExists($dir & $subdir)
		If $ret = 0  Or @error Then
			MsgError("Dir does not exist", $dir & $subdir, $ret, @error)
		EndIf

		$ret = DirRemove($dir & $subdir)
		If $ret = 0  Or @error Then
			MsgError("Dir Not removed", $dir & $subdir, $ret, @error)
		EndIf

		$ret = DirRemove($dir, 1)
		If $ret = 0  Or @error Then MsgError("Dir Not removed", $dir, $ret, @error)

		$dir = "c:\some"
		$ret = DirRemove($dir, 1)
		If $ret = 0  Or @error Then MsgError("Dir Not removed", $dir, $ret, @error)
	EndIf
EndFunc

Func MsgError($title, $dir, $ret, $error)
	MsgBox(0, $title, "dir = " & $dir & @CRLF & @CRLF & "return = " & $ret & " @error = " & $error)
EndFunc

Last edited 8 years ago by Jpm (previous) (diff)

comment:2 Changed 8 years ago by wraithdu

That's not correct. The directory is not created properly. *A* directory *is* created, but it's not the one specified in the function. The path is truncated to 255 chars and a 255 char length directory is created.

In fact FileExists does work correctly on paths longer than 255 chars, but you have to create them via other means to test this.

Here is the path from your test as it was created:

C:\some\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\lo

It is 255 chars long.

Try this for comparison. You'll have to rename some directories to remove the final path since it will be too long (>255).

$sPath = "\\?\C:\some\"
$sPath &= "really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\really\long\path"
;
ConsoleWrite("Path len: " & StringLen($sPath) & " : " & $sPath & @CRLF)
$ret = _LFC_CreateDirectory($sPath)
ConsoleWrite("Return: " & $ret & @CRLF)
ConsoleWrite("Dir exists: " & FileExists($sPath) & @CRLF)
; to be sure...
ConsoleWrite("Dir exists2: " & FileExists($sPath & "nothere") & @CRLF)
ConsoleWrite("Dir exists3: " & FileExists(StringTrimRight($sPath, 2)) & @CRLF)
; remove fails
ConsoleWrite("Removed: " & DirRemove($sPath, 1) & @CRLF)

Func _LFC_CrackPath($sPath)
	; make sure path is terminated
	If StringRight($sPath, 1) <> "\" Then $sPath &= "\"
	; get prefix
	Local $aRet = StringRegExp($sPath, "(?i)^\\\\\?\\unc\\|\\\\\?\\|\\\\", 1)
	If Not IsArray($aRet) Then
		$aRet = ""
	Else
		$aRet = $aRet[0]
	EndIf
	; capture and remove the root
	If ($aRet = "\\") Or ($aRet = "\\?\UNC\") Then
		; UNC network path
		$aRet = StringRegExp($sPath, "(?i)^(" & StringReplace(StringReplace($aRet, "\", "\\"), "?", "\?") & ".*?\\.*?\\)", 1)
	Else
		; $aRet = "" or \\?\ => local path
		Local $iTrim = StringLen($aRet) + 3
		Local $aRet[1] = [StringLeft($sPath, $iTrim)]
	EndIf
	Local $aPath = StringTrimLeft($sPath, StringLen($aRet[0]))
	; check if path given was just a root
	If $aPath <> "" Then
		; crack path, prepend \ to get first segment
		$aPath = StringRegExp("\" & $aPath, "\\(.*?)(?=\\)", 3)
		ReDim $aRet[UBound($aPath) + 1]
		For $i = 0 To UBound($aPath) - 1
			$aRet[$i + 1] = $aPath[$i]
		Next
	EndIf
	;
	Return $aRet
EndFunc   ;==>_LFC_CrackPath

Func _LFC_CreateDirectory($sPath)
	; check path already exists
	If FileExists($sPath) Then
		; check if it is a direcotry
		If StringInStr(FileGetAttrib($sPath), "D") Then
			Return 1
		Else
			Return SetError(1, 0, 0)
		EndIf
	EndIf
	;
	Local $aPath = _LFC_CrackPath($sPath)
	; make sure root exists
	Local $sSegment = $aPath[0]
	If Not FileExists($sSegment) Then Return SetError(2, 0, 0)
	Local $ret
	For $i = 1 To UBound($aPath) - 1
		$sSegment &= $aPath[$i] & "\"
		If Not FileExists($sSegment) Then
			; create sub-path
			$ret = DllCall("kernel32.dll", "bool", "CreateDirectoryW", "wstr", $sSegment, "ptr", 0)
			If @error Or Not $ret[0] Then Return SetError(3, 0, 0)
		EndIf
	Next
	Return 1
EndFunc   ;==>_LFC_CreateDirectory

comment:3 Changed 8 years ago by Jpm

I understand, I can add that the removal is only possible if the full path of the last created directory if less than ~260 chars.
Explorer itself cannot remove it (I am running Win7 Sp1 32-bit)

comment:4 follow-up: Changed 8 years ago by wraithdu

So you're confirming this is an error/bug as to the behavior of DirCreate? Because DirCreate, as I showed, is not creating the extra long path properly.

If so, thank you :)

comment:5 in reply to: ↑ 4 Changed 8 years ago by Jpm

Replying to wraithdu:

So you're confirming this is an error/bug as to the behavior of DirCreate? Because DirCreate, as I showed, is not creating the extra long path properly.

If so, thank you :)

Before your ticket I already warn the other dev that we may have to support

\\?\

Perhaps they will do something. I am a banned Dev Thanks to Valik

comment:6 Changed 8 years ago by Valik

JPM, you are not a developer any longer, please stop acting like one.

Yes, JP brought it to our attention that long paths are not supported. All path related functions use a hard-coded buffer length of MAX_PATH. I detest hard-coded buffer sizes and I know that it needs changed to support variable length paths. The problem is I'm trying to get a release out before we all die and we can't make significant changes like this just weeks before said release.

It was fine to create the ticket but all this discussion with JP has been pointless. Long paths will be supported at some point after the next release and this ticket should be irrelevant at that time.

comment:7 Changed 8 years ago by wraithdu

Not a problem, thank you for the response.

Would it be reasonable to make a smaller change, and have DirCreate return an error when given a path greater than the buffer size, instead of returning success and creating some unknown truncated path? It's this latter behavior that should ideally be avoided.

comment:8 Changed 7 years ago by Jon

  • Resolution set to Rejected
  • Status changed from new to closed

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.