Recent

Author Topic: Test if I could write to a certain file  (Read 8140 times)

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Test if I could write to a certain file
« Reply #15 on: August 04, 2021, 09:06:28 pm »
Duh! I went to the help to look for a DeleteFile() (and see its sintax) and on coming back ... forgot to include it! Sorry, man :-[
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Kays

  • Sr. Member
  • ****
  • Posts: 353
  • Whasup!?
    • KaiBurghardt.de
Re: Test if I could write to a certain file
« Reply #16 on: August 04, 2021, 09:39:14 pm »
does anyone have a test routine ready to determine whether I am allowed to write to a specific file?
No. There are many ways how a user gets the privilege to write a file. While you can easily check for the classic Unix file mode yourself, it is also possible that you are allowed to write to a file by virtue of group membership. Then you had to determine whether a user is member of that group. There are also ACLs, which can also grant permissions. Last but not least, it is also possible your program is run with superuser/root privileges. You see, you cannot positively check all of this.

You will necessarily have to involve the operating system, because only the OS will have the final say when you eventually attempt writing to that file. So a method is to simply try writing to that file.
The caveat is, that this file may be already existent, but should not be changed (yet).
Yes, instead of rewrite, which will clear the file, you can use append (Delphi/FPC)/extend (Extended Pascal):
Code: Pascal  [Select][+][-]
  1. {**
  2.         \brief tests whether a file is writable
  3.        
  4.         \param f a text file that is already
  5.                 bound to file name (`assign`)
  6.         \return true if existent file is writable
  7. }
  8. function isWritable(var f: text): Boolean;
  9. begin
  10.         { remove any unchecked errors from previous code }
  11.         InOutRes := 0;
  12.        
  13.         {$push}
  14.                 {$IOChecks off}
  15.                 { this may potentially cause RTE 5 "file access denied" }
  16.                 append(f);
  17.         {$pop}
  18.        
  19.         isWritable := IOResult = 0;
  20.        
  21.         { close fails on an _un_opened file }
  22.         if isWritable then
  23.         begin
  24.                 close(f);
  25.         end;
  26. end;
It’s up to you to implement the part checking for not yet existent files. The RTE 2 “file not found” tells you that.

This method may potentially alter meta-data, like the access time. My tests on a Linux system here showed that the data/payload modification time was not altered, even if there was write(f, '');.

PS: Sorry, I just see in an strace that write(f, '') won’t even bother the OS, so it’s no surprise it does not change the mtime.
« Last Edit: August 04, 2021, 10:29:32 pm by Kays »
Yours Sincerely
Kai Burghardt

BobDog

  • Full Member
  • ***
  • Posts: 122
Re: Test if I could write to a certain file
« Reply #17 on: August 05, 2021, 01:44:16 am »

Check the file attributes-Windows.
Code: Pascal  [Select][+][-]
  1.    program arrtibutes;
  2.   uses
  3.   process;
  4.  
  5.     procedure getattributes(filename:ansistring);
  6.   var
  7.   s,chr,g:ansistring;
  8.   i:integer;
  9.   begin
  10.    (RunCommand('attrib '+ filename,s));
  11.    write(s);
  12.    if s[1]='F' then exit;
  13.    for i:=1 to 20 do
  14.    begin
  15.    chr:=s[i];
  16.    case chr of
  17.    'A':writeln('archive');
  18.    'C':writeln('compressed');
  19.    'H':writeln('hidden');
  20.    'I':writeln('not content indexed');
  21.    'O':writeln('offline');
  22.    'P':writeln('pinned (Windows 10 and OneDrive only)');
  23.    'R':writeln('readonly');
  24.    'S':writeln('system');
  25.    'T':writeln('temperory');
  26.    'U':writeln('unpinned (Windows 10 and OneDrive only)');
  27.    'V':writeln('integrity (Windows Server 2012R2+ ReFS only0');
  28.    'X':writeln('no_scrub_data (Windows Server 2012R2+ ReFS only)');
  29.    end
  30.    end;
  31.    writeln('----------------');
  32.   end;
  33.  
  34.  
  35.   begin
  36. getattributes('C:\Windows\notepad.exe');
  37. getattributes('C:\Windows\system32\attrib.exe');
  38. getattributes('C:\pagefile.sys');
  39. getattributes('C:\hiberfil.sys');
  40. getattributes('C:\swapfile.sys');
  41. getattributes('Nonesuch.txt');
  42.  writeln('Press return to finish . . .');
  43. readln;
  44. end.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 985
    • Lebeau Software
Re: Test if I could write to a certain file
« Reply #18 on: August 05, 2021, 04:03:09 am »
your code was a bit erroneous. I have corrected that (see below).
Nevertheless: Thanks very much, because now it WORKS ;-))

Except, it doesn't actually work, at least not the way you originally asked for it:

does anyone have a test routine ready to determine whether I am allowed to write to a specific file?

If the caller actually passed in a full file path, the code is not creating/opening the requested file anymore, it merely determines the parent folder of the file and then creates its own random file in that folder, completely ignoring the requested file.  Essentially, you have now created a FolderIsWriteable() type of function (and in that scenario, using MakeRandomString() is overkill, when you can just use GetTempFileName() instead, or a GUID).

For Windows, I would opt for something more like the following instead.  If you need something that is cross-platform, {$IFDEF} the code accordingly:

Code: Pascal  [Select][+][-]
  1. uses
  2.   .., Windows;
  3.  
  4. function FileIsWriteable(fn: String; KeepFileIfCreatedNew: Boolean = False) : Boolean;
  5. var
  6.   h: THandle;
  7.   Err: DWord;
  8. begin
  9.   h := CreateFile(PChar(fn), GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  10.   Result := h <> INVALID_HANDLE_VALUE;
  11.   if Result then begin
  12.     Err := GetLastError;
  13.     CloseHandle(h);
  14.     if (Err <> ERROR_ALREADY_EXISTS) and (not KeepFileIfCreatedNew) then
  15.       DeleteFile(PChar(fn));
  16.   end;
  17. end;
  18.  

If CreateFile() succeeds, you are guaranteed to have write access to the file via the returned THandle only.  Once you close that THandle, all bets are off, you lose any guarantee that you will still have write access to the same file at a later time (ie, someone else could open the file and decide not to share write access to it, etc).

The mere presence of a FileIsWriteable()-like function introduces a TOCTOU race condition that you really should not be introducing to begin with.  If you want write access to a file, then just open the file for writing AT THE TIME you want to write to it, then actually write to it (and keep it open for later writes, if needed), and then close it when you are done using it.  Handle any failure that may occur during that time as needed.

You know the old saying "It is better to ask for forgiveness than permission"?  Well, that applies here, too.  If you want to do something (write to a file, etc), then just go ahead and attempt to do it, and handle the consequences as needed.  Don't try asking for permission first.
« Last Edit: August 05, 2021, 04:13:11 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

dietmar

  • Full Member
  • ***
  • Posts: 139
Re: Test if I could write to a certain file
« Reply #19 on: August 05, 2021, 10:31:20 pm »
Thank you for your answer! I will have to think about that...

--Dietmar
Lazarus 2.2.0RC1 with FPC 3.2.2 on Windows10 64Bit

 

TinyPortal © 2005-2018