Pārlūkot izejas kodu

+ support for file locking for *nix systems based on flock (mantis #13012)

git-svn-id: trunk@12630 -
Jonas Maebe 16 gadi atpakaļ
vecāks
revīzija
a730ee7f10
3 mainītis faili ar 232 papildinājumiem un 5 dzēšanām
  1. 1 0
      .gitattributes
  2. 46 5
      rtl/unix/sysutils.pp
  3. 185 0
      tests/test/units/sysutils/tfile2.pp

+ 1 - 0
.gitattributes

@@ -8151,6 +8151,7 @@ tests/test/units/sysutils/texec1.pp svneol=native#text/plain
 tests/test/units/sysutils/texec2.pp svneol=native#text/plain
 tests/test/units/sysutils/textractquote.pp svneol=native#text/plain
 tests/test/units/sysutils/tfile1.pp svneol=native#text/plain
+tests/test/units/sysutils/tfile2.pp svneol=native#text/plain
 tests/test/units/sysutils/tfilename.pp svneol=native#text/plain
 tests/test/units/sysutils/tfloattostr.pp -text
 tests/test/units/sysutils/tlocale.pp svneol=native#text/plain

+ 46 - 5
rtl/unix/sysutils.pp

@@ -176,11 +176,51 @@ Begin
 End;
 
 
-Function FileOpen (Const FileName : string; Mode : Integer) : Longint;
+Function DoFileLocking(Handle: Longint; Mode: Integer) : Longint;
+var
+  lockop: cint;
+  lockres: cint;
+begin
+  DoFileLocking:=Handle;
+  if (Handle>=0) then
+    begin
+      case (mode and (fmShareExclusive or fmShareDenyWrite or fmShareDenyRead)) of
+        fmShareCompat,
+        fmShareExclusive:
+          lockop:=LOCK_EX or LOCK_NB;
+        fmShareDenyWrite:
+          lockop:=LOCK_SH or LOCK_NB;
+        fmShareDenyNone:
+          exit;
+        else
+          begin
+            { fmShareDenyRead does not exit under *nix, only shared access
+              (similar to fmShareDenyWrite) and exclusive access (same as
+              fmShareExclusive)
+            }
+            FpClose(Handle);
+            DoFileLocking:=-1;
+            exit;
+          end;
+      end;
+      repeat
+        lockres:=fpflock(Handle,lockop);
+      until lockres<>ESysEIntr;
+      if (lockres<>0) then
+        begin
+          FpClose(Handle);
+          DoFileLocking:=-1;
+          exit;
+        end;
+    end;
+end;
 
-Var LinuxFlags : longint;
 
-BEGIN
+Function FileOpen (Const FileName : string; Mode : Integer) : Longint;
+
+Var
+  LinuxFlags : longint;
+begin
   LinuxFlags:=0;
   Case (Mode and 3) of
     0 : LinuxFlags:=LinuxFlags or O_RdOnly;
@@ -188,7 +228,8 @@ BEGIN
     2 : LinuxFlags:=LinuxFlags or O_RdWr;
   end;
   FileOpen:=fpOpen (pointer(FileName),LinuxFlags);
-  //!! We need to set locking based on Mode !!
+
+  FileOpen:=DoFileLocking(FileOpen, Mode);
 end;
 
 
@@ -201,7 +242,7 @@ end;
 
 Function FileCreate (Const FileName : String;Mode : Longint) : Longint;
 
-BEGIN
+begin
   FileCreate:=fpOpen(pointer(FileName),O_RdWr or O_Creat or O_Trunc,Mode);
 end;
 

+ 185 - 0
tests/test/units/sysutils/tfile2.pp

@@ -0,0 +1,185 @@
+{$ifdef fpc}
+{$mode objfpc}
+{$h+}
+{$endif}
+
+uses
+  SysUtils;
+
+{$ifndef fpc}
+const
+  fmsharecompat = cardinal(0);
+  fsFromBeginning = cardinal(0);
+{$endif}
+
+var
+  l,l2: longint;
+begin
+  try
+    try
+      l:=filecreate('tfile2.dat');
+      if (l<0) then
+        raise exception.create('unable to create file');
+      fileclose(l);
+      l:=fileopen('tfile2.dat',fmopenread);
+      if (filewrite(l,l,sizeof(l))>0) then
+        raise exception.create('writing to read-only file succeeded');
+      fileclose(l);
+      deletefile('tfile2.dat');
+
+
+      l:=filecreate('tfile2.dat');
+      if (l<0) then
+        raise exception.create('unable to create file (2)');
+      fileclose(l);
+      l:=fileopen('tfile2.dat',fmopenwrite);
+      if (filewrite(l,l,sizeof(l))<>sizeof(l)) then
+        raise exception.create('writing to write-only file failed');
+      if (fileseek(l,0,fsFromBeginning)<>0) then
+        raise exception.create('seeking write-only file failed');
+      if (fileread(l,l2,sizeof(l))>=0) then
+        raise exception.create('reading from write-only file succeeded');
+      fileclose(l);
+
+      l:=fileopen('tfile2.dat',fmopenread or fmShareDenyWrite);
+      if (l<0) then
+        raise exception.create('unable to open file in read-only mode and fmShareDenyWrite mode');
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyWrite);
+      if (l2 < 0) then
+        raise exception.create('opening two files as read-only with fmShareDenyWrite failed');
+      fileclose(l2);
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareExclusive);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening file first as read-only with fmShareDenyWrite, and then again as fmopenread with fmShareExclusive succeeded');
+        end;
+      fileclose(l);
+
+
+      l:=fileopen('tfile2.dat',fmopenwrite or fmShareExclusive);
+      if (l<0) then
+        raise exception.create('unable to open file in write-only and fmShareExclusive mode');
+      l2:=fileopen('tfile2.dat',fmopenwrite or fmShareExclusive);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening two files as write-only with fmShareExclusive succeeded');
+        end;
+      l2:=fileopen('tfile2.dat',fmopenwrite or fmShareDenyWrite);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening file first as write-only with fmShareExclusive, and then again as fmopenwrite with fmShareDenyWrite succeeded');
+        end;
+      fileclose(l);
+
+
+      l:=fileopen('tfile2.dat',fmopenread or fmShareExclusive);
+      if (l<0) then
+        raise exception.create('unable to open file in read-only and fmShareExclusive mode');
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareExclusive);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening two files as read-only with fmShareExclusive succeeded');
+        end;
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyWrite);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening file first as read-only with fmShareExclusive, and then again as fmopenread with fmShareDenyWrite succeeded');
+        end;
+      fileclose(l);
+
+
+      l:=fileopen('tfile2.dat',fmopenread);
+      if (l<0) then
+        raise exception.create('unable to open file in read-only mode (2)');
+      l2:=fileopen('tfile2.dat',fmopenread);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening two files as read-only without sharing specified succeeded (should both be exclusive)');
+        end;
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyWrite);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening file first as read-only without sharing mode (should be exclusive), and then again as fmopenread with fmShareDenyWrite succeeded');
+        end;
+      fileclose(l);
+
+
+      { should be same as fmShareExclusive }
+      l:=fileopen('tfile2.dat',fmopenread or fmShareCompat);
+      if (l<0) then
+        raise exception.create('unable to open file in read-only and fmShareCompat mode');
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareCompat);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening two files as read-only with fmShareCompat succeeded');
+        end;
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyWrite);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening file first as read-only with fmShareCompat, and then again as fmopenread with fmShareDenyWrite succeeded');
+        end;
+      fileclose(l);
+
+
+      l:=fileopen('tfile2.dat',fmopenread or fmShareDenyNone);
+      if (l<0) then
+        raise exception.create('unable to open file in read-only mode and fmShareDenyNone mode');
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyNone);
+      if (l2 >= 0) then
+        raise exception.create('opening two files as read-only with fmShareDenyNone failed');
+      fileclose(l2);
+      { unix-specific that this fails? }
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyWrite);
+      if (l2 >= 0) then
+        raise exception.create('opening two files as read-only with fmShareDenyNone and then fmShareDenyWrite succeeded');
+      fileclose(l2);
+      { unix-specific that this fails? }
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareExclusive);
+      if (l2 >= 0) then
+        raise exception.create('opening two files as read-only with fmShareDenyNone and then fmShareExclusive succeeded');
+      fileclose(l2);
+      fileclose(l);
+
+      l:=fileopen('tfile2.dat',fmopenread or fmShareDenyWrite);
+      if (l<0) then
+        raise exception.create('unable to open file in read-only mode and fmShareDenyWrite mode (2)');
+      { unix-specific that this fails? }
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyNone);
+      if (l2 >= 0) then
+        raise exception.create('opening files as read-only with fmShareDenyWrite and then fmShareDenyNone succeeded');
+      fileclose(l2);
+      fileclose(l);
+
+
+      l:=fileopen('tfile2.dat',fmopenwrite or fmShareDenyNone);
+      if (l<0) then
+        raise exception.create('unable to open file in write-only mode and fmShareDenyNone mode');
+      l2:=fileopen('tfile2.dat',fmopenread or fmShareDenyNone);
+      if (l2 >= 0) then
+        begin
+          fileclose(l2);
+          raise exception.create('opening two files as read/write-only with fmShareDenyNone succeeded');
+        end;
+      fileclose(l2);
+
+    except
+      on e: exception do
+        begin
+          writeln(e.message);
+          exitcode:=1;
+        end;
+    end;
+  finally
+    fileclose(l);
+    deletefile('tfile2.dat');
+  end;
+end.