Forum > Windows

Test if I could write to a certain file

<< < (4/4)

lucamar:
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 :-[

Kays:

--- Quote from: dietmar on August 01, 2021, 09:38:36 pm ---does anyone have a test routine ready to determine whether I am allowed to write to a specific file?
--- End quote ---
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.
--- Quote from: dietmar on August 01, 2021, 09:38:36 pm ---The caveat is, that this file may be already existent, but should not be changed (yet).
--- End quote ---
Yes, instead of rewrite, which will clear the file, you can use append (Delphi/FPC)/extend (Extended Pascal):
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---{**        \brief tests whether a file is writable                \param f a text file that is already                bound to file name (`assign`)        \return true if existent file is writable}function isWritable(var f: text): Boolean;begin        { remove any unchecked errors from previous code }        InOutRes := 0;                {$push}                {$IOChecks off}                { this may potentially cause RTE 5 "file access denied" }                append(f);        {$pop}                isWritable := IOResult = 0;                { close fails on an _un_opened file }        if isWritable then        begin                close(f);        end;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.

BobDog:

Check the file attributes-Windows.

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---   program arrtibutes;  uses  process;     procedure getattributes(filename:ansistring);  var  s,chr,g:ansistring;  i:integer;  begin   (RunCommand('attrib '+ filename,s));   write(s);   if s[1]='F' then exit;   for i:=1 to 20 do   begin   chr:=s[i];   case chr of   'A':writeln('archive');   'C':writeln('compressed');   'H':writeln('hidden');   'I':writeln('not content indexed');   'O':writeln('offline');   'P':writeln('pinned (Windows 10 and OneDrive only)');   'R':writeln('readonly');   'S':writeln('system');   'T':writeln('temperory');   'U':writeln('unpinned (Windows 10 and OneDrive only)');   'V':writeln('integrity (Windows Server 2012R2+ ReFS only0');   'X':writeln('no_scrub_data (Windows Server 2012R2+ ReFS only)');   end   end;   writeln('----------------');  end;    begingetattributes('C:\Windows\notepad.exe');getattributes('C:\Windows\system32\attrib.exe');getattributes('C:\pagefile.sys');getattributes('C:\hiberfil.sys');getattributes('C:\swapfile.sys');getattributes('Nonesuch.txt'); writeln('Press return to finish . . .');readln;end.

Remy Lebeau:

--- Quote from: dietmar on August 04, 2021, 08:38:07 pm ---your code was a bit erroneous. I have corrected that (see below).
Nevertheless: Thanks very much, because now it WORKS ;-))

--- End quote ---

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


--- Quote from: dietmar on August 01, 2021, 09:38:36 pm ---does anyone have a test routine ready to determine whether I am allowed to write to a specific file?

--- End quote ---

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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---uses  .., Windows; function FileIsWriteable(fn: String; KeepFileIfCreatedNew: Boolean = False) : Boolean;var  h: THandle;  Err: DWord;begin  h := CreateFile(PChar(fn), GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);  Result := h <> INVALID_HANDLE_VALUE;  if Result then begin    Err := GetLastError;    CloseHandle(h);    if (Err <> ERROR_ALREADY_EXISTS) and (not KeepFileIfCreatedNew) then      DeleteFile(PChar(fn));  end;end; 
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.

dietmar:
Thank you for your answer! I will have to think about that...

--Dietmar

Navigation

[0] Message Index

[*] Previous page

Go to full version