View Issue Details

IDProjectCategoryView StatusLast Update
0035269FPCRTLpublic2019-03-31 19:47
ReporterAlfredAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version3.3.1Product Buildtrunk latest 
Target Version3.2.0Fixed in Version3.3.1 
Summary0035269: Zipper is unable to create files with filename length > 256 characters
DescriptionWhen using the FPC zipper on Windows 10 to unpack an archive containing files with filename > 256 characters, it fails with an EFCreateError.

See "Additional Information".

After the proposed change, (un)Zipper works as expected.
Additional InformationTo allow long filenames, the SysUtils FileCreate function has to be adapted according to:
https://docs.microsoft.com/nl-nl/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation

Example:

Function FileCreate (Const FileName : UnicodeString; ShareMode : Integer; Rights : Integer) : THandle;
var
  s : unicodestring;
begin
  s := ExpandFileName (FileName);
  s := '\\?\' + s + #0;
  Result := CreateFileW(PwideChar(s), GENERIC_READ or GENERIC_WRITE,
                       dword(ShareModes[(ShareMode and $F0) shr 4]), nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
end;
TagsNo tags attached.
Fixed in Revision41807
FPCOldBugId
FPCTarget
Attached Files
  • zipper.longfilenames.diff (1,193 bytes)
    Index: packages/paszlib/src/zipper.pp
    ===================================================================
    --- packages/paszlib/src/zipper.pp	(revision 41788)
    +++ packages/paszlib/src/zipper.pp	(working copy)
    @@ -633,16 +633,33 @@
     
     
       constructor TFileStream.Create(const AFileName: rawbytestring; Mode: Word; Rights: Cardinal);
    +    {$ifdef Windows}
    +    function FixLongFilename(const Fn: RawByteString): RawByteString;
    +    begin
    +      Result := Fn;
    +      if (Length(Fn)>MAX_PATH) and not ((Pos('\\?\', Fn)=1) or (Pos('\\.\', Fn)=1) or (Pos('\\?\UNC\', Fn)=1)) then
    +        begin
    +          if (Pos('\\', Fn)=1) then
    +            Insert('?\UNC\',Result,3)
    +          else
    +            Result:='\\?\'+Fn;
    +        end;
    +    end;
    +    {$endif}
     
       Var
         H : Thandle;
     
       begin
    +    {$ifdef Windows}
    +    FFileName:=FixLongFilename(AFileName);
    +    {$else}
         FFileName:=AFileName;
    +    {$endif}
         If (Mode and fmCreate) > 0 then
    -      H:=FileCreate(AFileName,Mode,Rights)
    +      H:=FileCreate(FFileName,Mode,Rights)
         else
    -      H:=FileOpen(AFileName,Mode);
    +      H:=FileOpen(FFileName,Mode);
     
         If (THandle(H)=feInvalidHandle) then
           If Mode=fmcreate then
    

Activities

Michael Van Canneyt

2019-03-25 08:47

administrator   ~0115033

I don't think this is a correct solution.
1. If the original filename is less than 255, you should IMHO not do anything.
2. If the originl filename already contains \\?\ you should also not do anything.
3. If the original filename contains an UNC path, you need to prepend \\?\UNC\ to the path, as per the page you refer to.

Alfred

2019-03-25 09:15

reporter   ~0115034

Agreed.

The source-changes shown are only added as a proof that, in this simple (zipper) case, this works. And also for building the compiler.

A more generic (working) change would be much preferred !
But that will be beyond my knowledge.

Bart Broersma

2019-03-25 11:23

reporter   ~0115036

Simple checks?

1: Length(Filename)>255
2: Pos('\\?\', Filename)=1
3:
function IsUNCPath(const Path: String): Boolean;
begin
  Result := (Length(Path) > 2) and (Path[1] in AllowDirectorySeparators) and (Path[2] in AllowDirectorySeparators);
end;

Bart Broersma

2019-03-27 16:14

reporter   ~0115083

Last edited: 2019-03-27 16:15

View 2 revisions

I don't think this should be fixed in FileCreate()?
Applications that have to deal with this scenario need to handle that themselfs IMO.

Bart Broersma

2019-03-27 16:55

reporter   ~0115086

FWIW: System.Rewrite and System.Reset don't give any error when filename is too long (InOutRes is zero).

Bart Broersma

2019-03-31 17:48

reporter  

zipper.longfilenames.diff (1,193 bytes)
Index: packages/paszlib/src/zipper.pp
===================================================================
--- packages/paszlib/src/zipper.pp	(revision 41788)
+++ packages/paszlib/src/zipper.pp	(working copy)
@@ -633,16 +633,33 @@
 
 
   constructor TFileStream.Create(const AFileName: rawbytestring; Mode: Word; Rights: Cardinal);
+    {$ifdef Windows}
+    function FixLongFilename(const Fn: RawByteString): RawByteString;
+    begin
+      Result := Fn;
+      if (Length(Fn)>MAX_PATH) and not ((Pos('\\?\', Fn)=1) or (Pos('\\.\', Fn)=1) or (Pos('\\?\UNC\', Fn)=1)) then
+        begin
+          if (Pos('\\', Fn)=1) then
+            Insert('?\UNC\',Result,3)
+          else
+            Result:='\\?\'+Fn;
+        end;
+    end;
+    {$endif}
 
   Var
     H : Thandle;
 
   begin
+    {$ifdef Windows}
+    FFileName:=FixLongFilename(AFileName);
+    {$else}
     FFileName:=AFileName;
+    {$endif}
     If (Mode and fmCreate) > 0 then
-      H:=FileCreate(AFileName,Mode,Rights)
+      H:=FileCreate(FFileName,Mode,Rights)
     else
-      H:=FileOpen(AFileName,Mode);
+      H:=FileOpen(FFileName,Mode);
 
     If (THandle(H)=feInvalidHandle) then
       If Mode=fmcreate then

Bart Broersma

2019-03-31 17:49

reporter   ~0115147

Naive implementation of a patch attached in zipper.longfilenames.diff.

Michael Van Canneyt

2019-03-31 18:23

administrator   ~0115148

Applied (slightly modified) patch, pending fundamental solution in sysutils.

Thanks for the patch !

Alfred

2019-03-31 19:47

reporter   ~0115150

Working.
Thanks !

Issue History

Date Modified Username Field Change
2019-03-25 07:40 Alfred New Issue
2019-03-25 08:47 Michael Van Canneyt Note Added: 0115033
2019-03-25 09:15 Alfred Note Added: 0115034
2019-03-25 11:23 Bart Broersma Note Added: 0115036
2019-03-27 16:14 Bart Broersma Note Added: 0115083
2019-03-27 16:15 Bart Broersma Note Edited: 0115083 View Revisions
2019-03-27 16:55 Bart Broersma Note Added: 0115086
2019-03-31 17:48 Bart Broersma File Added: zipper.longfilenames.diff
2019-03-31 17:49 Bart Broersma Note Added: 0115147
2019-03-31 18:23 Michael Van Canneyt Fixed in Revision => 41807
2019-03-31 18:23 Michael Van Canneyt Note Added: 0115148
2019-03-31 18:23 Michael Van Canneyt Status new => resolved
2019-03-31 18:23 Michael Van Canneyt Fixed in Version => 3.3.1
2019-03-31 18:23 Michael Van Canneyt Resolution open => fixed
2019-03-31 18:23 Michael Van Canneyt Assigned To => Michael Van Canneyt
2019-03-31 18:23 Michael Van Canneyt Target Version => 3.2.0
2019-03-31 19:47 Alfred Note Added: 0115150
2019-03-31 19:47 Alfred Status resolved => closed