7

How can I check whether a directory is writable by the executing user from a batch script?

Here's what I've tried so far:

> cd "%PROGRAMFILES%"

> echo. > foo
Access is denied.
> echo %ERRORLEVEL%
0

Ok, so how about...

> copy NUL > foo
Access is denied.
> echo %ERRORLEVEL%
0

Not that either? Then what about...

> copy foo bar
Access is denied.
        0 file(s) copied.
> echo %ERRORLEVEL%
1

This works, but it breaks if the file doesn't exist.

I've read something about internal commands not setting ERRORLEVEL, but copy obviously seems to do so in the last case.

Ryan Gates
  • 4,442
  • 5
  • 49
  • 87
batshcrazy
  • 71
  • 1
  • 1
  • 2
  • 1
    oh yeah... windows environment will piss you off with that. Many commands do not return %errorlevel% correctly... as well as many installed applications that use command line arguments. =D – Anthony Miller Sep 01 '11 at 15:49
  • 1
    haha, yeah... not to mention the thin proverbial ice that is quotation and paths containing spaces. I've resorted to using 8.3 pathnames. ;) – batshcrazy Sep 01 '11 at 15:54
  • 1
    you might get more luck with this question in SO sister site .. superuser.com – Ian G Sep 01 '11 at 15:58
  • What is the `something` you read about internal commands? Do you happen to have a link handy? – Ryan Gates Nov 27 '12 at 19:19
  • this helped me a lot [how-to-check-in-command-line-if-a-given-file-or-directory-is-locked-used-by-any][1] [1]: http://stackoverflow.com/questions/10518151/how-to-check-in-command-line-if-a-given-file-or-directory-is-locked-used-by-any – user2958279 Dec 03 '14 at 19:50

5 Answers5

4

Definitely running a command against it to find if its denied is the easy way to do it. You can also use CACLS to find exactly what the permissions are or aren't. Here's a sample.

In CMD type CACLS /?

CACLS "filename" will give you what the current permissions is allowed on the file. R = Read, W = Write, C = Change (same as write?), F = Full access.

EDIT: You can use directory name as well.

So to do a check, you would:

FOR /F "USEBACKQ tokens=2 delims=:" %%F IN (`CACLS "filename" ^| FIND "%username%"`) DO (
 IF "%%F"=="W" (SET value=true && GOTO:NEXT)
 IF "%%F"=="F" (SET value=true && GOTO:NEXT)
 IF "%%F"=="C" (SET value=true && GOTO:NEXT)
 SET value=false
)
ECHO This user does not have permissions to write to file.
GOTO:EOF
:NEXT
ECHO This user is able to write to file.
Anthony Miller
  • 13,960
  • 27
  • 69
  • 97
  • 3
    This is not correct, you can have access to a folder without having a specific ACE for your user (Admins, users, everyone etc) – Anders Sep 01 '11 at 19:42
  • @Anders Make a folder and run the following command on the folder `cacls foldername /P %computername%\%username%:R` Now CD into it and do `ECHO hello>file.txt` You'll get an access denied. – Anthony Miller Sep 01 '11 at 19:51
  • 1
    @Mechaflash: What does that have to do with anything? The problem in your code I'm talking about is the FIND "%username%" part. Just because a user is not listed does not mean they don't have write access (or other kinds of access). I can have a folder with just "Everyone:(OI)(CI)F" and I can write to it... – Anders Sep 01 '11 at 20:04
  • I see your point. However, if you check access rights for even original install folders (Documents and Settings for example), Everyone will only ever be assigned READ-ONLY unless altered. And if someone's set Everyone to have FULL or WRITE access to anything... they shouldn't be touching a computer =/ – Anthony Miller Sep 01 '11 at 20:18
  • @Mechaflash: I use Everyone etc in the ACL (not user or computer specific) on some USB drives, I guess I should walk away from the keyboard then... – Anders Sep 01 '11 at 21:25
  • *"The Cacls command can be run only on disk drives that use the NTFS file system."* – Samuel Harmer Feb 15 '13 at 10:24
  • ^ This was so 2011 ago =P Didn't know that lil tidbit however. – Anthony Miller Feb 15 '13 at 16:26
3

You can write copy %0 foo to copy the batch file itself.
This will always exist.

Remember to delete the file afterwards, and to make sure that you aren't overwriting an existing file by that name.

There ought to be a better way to do this, but I don't know of any.

EDIT: Better yet, try mkdir foo.
In case the batch file is running off a network (or if it's very large), this may be faster.

SLaks
  • 837,282
  • 173
  • 1,862
  • 1,933
1
set testdir=%programfiles%
set myguid={A4E30755-FE04-4ab7-BD7F-E006E37B7BF7}.tmp
set waccess=0
echo.> "%testdir%\%myguid%"&&(set waccess=1&del "%testdir%\%myguid%")
echo write access=%waccess%
Anders
  • 90,169
  • 12
  • 105
  • 156
1

i found that executing copy within the batch file echoed an error to STDERR, but left %ERRORLEVEL% untouched (still 0). so the workaround was to combine the command with a conditional execution of set.

copy /Y NUL "%FOLDER%\.writable" > NUL 2>&1 && set WRITEOK=1
IF DEFINED WRITEOK ( 
  rem ---- we have write access ----
  ...
 ) else (
  rem ---- we don't ----
  ...
)

this is tested on XP and 7 and seems to work reliably.

0

An extension to Mechaflash's answer, and solves the problem of overwriting the file by generating a unique filename for the "testing" file.

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "a=%~1"
SET "b="
SET "g=0"
:a
SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!@#$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
SET /A "d=0, e=1"
:b
IF "!c!" NEQ "" (
    IF "!c:~%d%,1!" NEQ "" (
        IF EXIST "!a!\!b!!c:~%d%,1!" (
            SET "c=!c:~0,%d%!!c:~%e%!"
        ) ELSE (
            SET /A "d=!d!+1, e=!e!+1"
        )
        GOTO :b
    )
)
IF "!c!" EQU "" (
    SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!@#$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
    :c
    IF "!c!" NEQ "" (
        IF "!c:~%d%,1!" NEQ "" (
            SET /A "d=!d!+1"
            GOTO :c
        )
    )
    SET /A "d=!d!-1"
    SET /A "f=%RANDOM%*!d!/32768"
    SET "b=!b!!c:~%f%,1!"
    GOTO :a
) ELSE (
    SET /A "d=!d!-1"
    SET /A "f=%RANDOM%*!d!/32768"
    SET "b=!b!!c:~%f%,1!"
)
((ECHO EXIT>"!a!\!b!" && SET "g=1") & IF EXIST "!a!\!b!" DEL /F "!a!\!b!") >NUL 2>&1
ENDLOCAL & (SET "a=%g%")
IF "%a%" EQU "1" ECHO TRUE

(%~1 is the input directory)
EDIT: If you want a more safe option

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "a=%~1"
SET "b="
SET "g=0"
:a
SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!@#$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
SET /A "d=0, e=1"
:b
IF "!c!" NEQ "" (
    IF "!c:~%d%,1!" NEQ "" (
        IF EXIST "!a!\!b!!c:~%d%,1!" (
            SET "c=!c:~0,%d%!!c:~%e%!"
        ) ELSE (
            SET /A "d=!d!+1, e=!e!+1"
        )
        GOTO :b
    )
)
IF "!c!" EQU "" (
    SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!@#$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
    :c
    IF "!c!" NEQ "" (
        IF "!c:~%d%,1!" NEQ "" (
            SET /A "d=!d!+1"
            GOTO :c
        )
    )
    SET /A "d=!d!-1"
    SET /A "f=%RANDOM%*!d!/32768"
    SET "b=!b!!c:~%f%,1!"
    GOTO :a
) ELSE (
    SET /A "d=!d!-1"
    SET /A "f=%RANDOM%*!d!/32768"
    SET "b=!b!!c:~%f%,1!"
)
IF EXIST "!a!\!b!" (
    SET "b=!b:~0,-1!"
    GOTO :a
) ELSE (
    ((ECHO EXIT>"!a!\!b!" && SET "g=1") & IF EXIST "!a!\!b!" DEL /F "!a!\!b!") >NUL 2>&1
)
ENDLOCAL & (SET "a=%g%")
IF "%a%" EQU "1" ECHO TRUE
Leo Miao
  • 7
  • 5