Jump to content

Looking for a non-invasive way to check if a file or directory is writable / deletable [Solved]


KaFu
 Share

Recommended Posts

HiHo Forum,

as the topic title already says, I'm looking for a non-invasive way to check if a file or directory is writable / deletable.

I drafted two methods which both work fine, but are invasive (file is changed).

Method1 tries to set or unset the Archive attribute. This is invasive (file is changed) but works for both files and directories.

Method2 tries to open files for write access (non-invasive!), but relies on creating files in directories (invasive again, *sigh*) to check the accessibility.

What I want to do with this function is to check whether the current user could delete the file or directory in question (> UAC).

Maybe someone else got an idea how to do this non-invasive for directories too :graduated:?

#include <WinAPI.au3>

Global $aFiles[4] = [@ScriptFullPath, @ScriptDir, @SystemDir, @SystemDir & "\user32.dll"]

#region Method1
For $i = 0 To 3
    ConsoleWrite(FileExists($aFiles[$i]) & @TAB & _FileWriteAccessible_Method1($aFiles[$i]) & @TAB & $aFiles[$i] & @CRLF)
Next
#endregion Method1

ConsoleWrite(@CRLF & @CRLF)

#region Method2
For $i = 0 To 3
    ConsoleWrite(FileExists($aFiles[$i]) & @TAB & _FileWriteAccessible_Method2($aFiles[$i]) & @TAB & $aFiles[$i] & @CRLF)
Next
#endregion Method2

Func _FileWriteAccessible_Method1($sFile)
    If Not FileExists($sFile) Then Return -1
    Local $iSuccess = 0
    Switch StringInStr(FileGetAttrib($sFile), "A", 2)
        Case False
            If FileSetAttrib($sFile, "+A") Then
                $iSuccess = 1
                FileSetAttrib($sFile, "-A")
            EndIf
        Case Else
            If FileSetAttrib($sFile, "-A") Then
                $iSuccess = 1
                FileSetAttrib($sFile, "+A")
            EndIf
    EndSwitch
    Return $iSuccess
EndFunc ;==>_FileWriteAccessible_Method1

Func _FileWriteAccessible_Method2($sFile)
    If Not FileExists($sFile) Then Return -1
    Local $iRes, $hFile, $sFile_Buf, $i = 0
    Switch StringInStr(FileGetAttrib($sFile), "D", 2)
        Case True
            $sFile_Buf = $sFile
            While FileExists($sFile)
                $i += 1
                $sFile = $sFile_Buf & "\Test_" & $i
            WEnd
            $hFile = _WinAPI_CreateFile($sFile, 0, 7)
            Switch _WinAPI_GetLastError()
                Case 0 ; ERROR_SUCCESS
                    $iRes = 1
                Case 5 ; ERROR_ACCESS_DENIED
                    $iRes = 0
                Case Else ; w000t?
                    $iRes = _WinAPI_GetLastError()
            EndSwitch
            _WinAPI_CloseHandle($hFile)
            FileDelete($sFile)
        Case Else
            $hFile = _WinAPI_CreateFile($sFile, 2, 7)
            Switch _WinAPI_GetLastError()
                Case 0 ; ERROR_SUCCESS
                    $iRes = 1
                Case 5 ; ERROR_ACCESS_DENIED
                    $iRes = 0
                Case Else ; w000t?
                    $iRes = _WinAPI_GetLastError()
            EndSwitch
            _WinAPI_CloseHandle($hFile)
    EndSwitch
    Return $iRes
EndFunc ;==>_FileWriteAccessible_Method2
Edited by KaFu
Link to comment
Share on other sites

Does:

FileGetAttrib ( "filename" )
 
Parameters
filename Filename (or directory) to check.
Return Value
Success: Returns a code string representing a files attributes.
Failure: Returns "" (blank string) and sets @error to 1.
 
Remarks
String returned could contain a combination of these letters "RASHNDOCT":
"R" = READONLY
"A" = ARCHIVE
"S" = SYSTEM
"H" = HIDDEN
"N" = NORMAL
"D" = DIRECTORY
"O" = OFFLINE
"C" = COMPRESSED (NTFS compression, not ZIP compression)
"T" = TEMPORARY

Not work?

Check the results for an "R" or are you also checking to see if it's in use as well?

010101000110100001101001011100110010000001101001011100110010000

001101101011110010010000001110011011010010110011100100001

My Android cat and mouse game
https://play.google.com/store/apps/details?id=com.KaosVisions.WhiskersNSqueek

We're gonna need another Timmy!

Link to comment
Share on other sites

Checking the file attributes for the R flag is not enough (see attached update), but of course should be added the the overall function, thanks for that hint :graduated:!

What I'm looking for is to check whether the OS restricts the access to the file, like UAC does in Vista+ for system files, or files of other users for which the current user only has read but not modify rights.

Edit: I skipped the first method setting the Archive attribute, just does not seem right to do this.

#include <WinAPI.au3>

Global $aFiles[5] = [@ScriptDir & "\_test.txt", @ScriptFullPath, @ScriptDir, @SystemDir, @SystemDir & "\user32.dll"]

FileWriteLine($aFiles[0], "Test")
FileSetAttrib($aFiles[0], "+R")

For $i = 0 To 4
    ConsoleWrite(_FileWriteAccessible($aFiles[$i]) & @TAB & StringInStr(FileGetAttrib($aFiles[$i]), "R", 2) & @TAB & $aFiles[$i] & @CRLF)
Next

FileSetAttrib($aFiles[0], "-R")
FileDelete($aFiles[0])

Func _FileWriteAccessible($sFile)
    ; Returns
    ;        0 =  Success, file is writeable and deletable
    ;        -1 = File does not exist
    ;        -2 = Other Error, check @extended
    ;        1 = File Attribute READONLY is set
    ;        2 = Access is restricted

    If Not FileExists($sFile) Then Return -1
    If StringInStr(FileGetAttrib($aFiles[$i]), "R", 2) Then Return 1
    Local $iSuccess = -2, $iSuccess_Extended = 0, $hFile, $sFile_Buf, $i = 0
    Switch StringInStr(FileGetAttrib($sFile), "D", 2)
        Case True
            $sFile_Buf = $sFile
            While FileExists($sFile)
                $i += 1
                $sFile = $sFile_Buf & "\Test_" & $i
            WEnd
            $hFile = _WinAPI_CreateFile($sFile, 0, 7)
            Switch _WinAPI_GetLastError()
                Case 0 ; ERROR_SUCCESS
                    $iSuccess = 0
                Case 5 ; ERROR_ACCESS_DENIED
                    $iSuccess = 2
                Case Else ; w000t?
                    $iSuccess = -2
                    $iSuccess_Extended = _WinAPI_GetLastError()
            EndSwitch
            _WinAPI_CloseHandle($hFile)
            FileDelete($sFile)
        Case Else
            $hFile = _WinAPI_CreateFile($sFile, 2, 7)
            Switch _WinAPI_GetLastError()
                Case 0 ; ERROR_SUCCESS
                    $iSuccess = 0
                Case 5 ; ERROR_ACCESS_DENIED
                    $iSuccess = 2
                Case Else ; w000t?
                    $iSuccess = -2
                    $iSuccess_Extended = _WinAPI_GetLastError()
            EndSwitch
            _WinAPI_CloseHandle($hFile)
    EndSwitch
    Return SetError(0, $iSuccess_Extended, $iSuccess)
EndFunc   ;==>_FileWriteAccessible
Edited by KaFu
Link to comment
Share on other sites

Well, there's got to be some list or attribute or something recorded somewhere that tells windows which files and folders are locked due to UAC...

010101000110100001101001011100110010000001101001011100110010000

001101101011110010010000001110011011010010110011100100001

My Android cat and mouse game
https://play.google.com/store/apps/details?id=com.KaosVisions.WhiskersNSqueek

We're gonna need another Timmy!

Link to comment
Share on other sites

Well, I guess this can be checked by accessing and parsing the ACL for each file (the hard way). As I don't need any detailed info I hoped there was some kind of work-around resulting in the same information I really need.

Link to comment
Share on other sites

Using the _WinAPI_CreateFileEx() function from Yashieds most excellent with the flag $FILE_FLAG_BACKUP_SEMANTICS to access directories seems to do the trick :graduated:...

#include <WinAPIEx.au3>

Global $aFiles[6] = [@ScriptDir & "\_test.txt", @ScriptFullPath, @ScriptDir, @SystemDir, @SystemDir & "\user32.dll", "c:\unkown.txt"]

FileWriteLine($aFiles[0], "Test")
FileSetAttrib($aFiles[0], "+R")

For $i = 0 To 5
    $iRes = _FileWriteAccessible($aFiles[$i])
    ConsoleWrite($iRes & @TAB & @error & @TAB & @extended & @TAB & $aFiles[$i] & @CRLF)
Next

FileSetAttrib($aFiles[0], "-R")
FileDelete($aFiles[0])

Func _FileWriteAccessible($sFile)
    ; Returns
    ;            1 = Success, file is writeable and deletable
    ;            0 = Failure
    ; @error
    ;            1 = Access Denied because of lacking access rights
    ;             2 = File is set "Read Only" by attribute
    ;            3 = File not found
    ;            4 = Unknown Api Error, check @extended

    Local $iSuccess = 0, $iError_Extended = 0, $iError = 0, $hFile
    ;$hFile = _WinAPI_CreateFileEx($sFile, $OPEN_EXISTING, $FILE_WRITE_DATA, BitOR($FILE_SHARE_DELETE, $FILE_SHARE_READ, $FILE_SHARE_WRITE), $FILE_FLAG_BACKUP_SEMANTICS)
    $hFile = _WinAPI_CreateFileEx($sFile, 3, 2, 7, 0x02000000)
    Switch _WinAPI_GetLastError()
        Case 0 ; ERROR_SUCCESS
            $iSuccess = 1
        Case 5 ; ERROR_ACCESS_DENIED
            If StringInStr(FileGetAttrib($sFile), "R", 2) Then
                $iError = 2
            Else
                $iError = 1
            EndIf
        Case 2 ; ERROR_FILE_NOT_FOUND
            $iError = 3
        Case Else ; w000t?
            $iError = 4
            $iError_Extended = _WinAPI_GetLastError()
    EndSwitch
    _WinAPI_CloseHandle($hFile)
    Return SetError($iError, $iError_Extended, $iSuccess)
EndFunc   ;==>_FileWriteAccessible
Edited by KaFu
Link to comment
Share on other sites

  • 1 year later...

I can't remember why I've changed it, but in the latest version of SMF the function looks like this, give it a try.

#include <WinAPIEx.au3>

Global $aFiles[6] = [@ScriptDir & "\_test.txt", @ScriptFullPath, @ScriptDir, @SystemDir, @SystemDir & "\user32.dll", "c:\unkown.txt"]

FileWriteLine($aFiles[0], "Test")
FileSetAttrib($aFiles[0], "+R")

For $i = 0 To 5
    $iRes = _FileWriteAccessible($aFiles[$i])
    ConsoleWrite($iRes & @TAB & $aFiles[$i] & @CRLF)
Next

FileSetAttrib($aFiles[0], "-R")
FileDelete($aFiles[0])

Func _FileWriteAccessible($sFile)
    ; Returns
    ;            1 = Success, file is writeable and deletable
    ;            2 = Access Denied because of lacking access rights OR because file is open by another process
    ;             3 = File is set "Read Only" by attribute
    ;            4 = File not found
    If Not FileExists("\\?\" & $sFile) Then Return 4 ; File not found
    If StringInStr(FileGetAttrib("\\?\" & $sFile), "R", 2) Then Return 3 ; Read-Only Flag set
    Local $hFile = _WinAPI_CreateFileEx("\\?\" & $sFile, 3, 2, 7, 0x02000000)
    If $hFile = 0 Then Return 2 ; File not accessible, UAC issue?
    _WinAPI_CloseHandle($hFile)
    Return 1 ; Success
EndFunc   ;==>_FileWriteAccessible
Edited by KaFu
Link to comment
Share on other sites

Saving this one to my snippets. I've used _WinAPI_IsWritable for determining directories in the past.

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Link to comment
Share on other sites

  • 4 years later...

Needed something like this recently, but for folders. In case someone else needs it:

#NoTrayIcon
#RequireAdmin
#include <Constants.au3>
#include <WinAPI.au3>
#include <WinAPIFiles.au3>

$path = @WindowsDir
MsgBox(0, '', _GetTargetState($path))

Func _GetTargetState($FileOrFolder)
   Local $hFile = 0
   If not StringInStr(FileGetAttrib($FileOrFolder), "D") Then
      $hFile = _WinAPI_CreateFile('\\.\' & $FileOrFolder, 2, 2+4, 2+4)
   Else
      _WinAPI_MoveFileEx($FileOrFolder, $FileOrFolder, BitOR($MOVE_FILE_COPY_ALLOWED, $MOVE_FILE_REPLACE_EXISTING))
   EndIf
   $error = _WinAPI_GetLastError()
   If $hFile <> 0 Then _WinAPI_CloseHandle($hFile)
   Switch $error
   Case 0
      ; writable
      Return 'writable'
   Case 2
      ; doesnt exist
      Return 'not exist'
   Case 5
      If StringInStr(FileGetAttrib($FileOrFolder), 'R') Then
         ; read only
         Return 'read only'
      Else
         ; denied access
         Return 'denied access'
      EndIf
   Case 32
      ; in use
      Return 'in use'
   Case Else
      Return 'unknown'
   EndSwitch
EndFunc

 

Edited by Nikolas92
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...