3

Question: How can I change file permissions on a Windows 10 PC with a Python script?

I have written a Python script that takes folders, which are created by proprietary software, and moves them to a network drive with shutil.move().

It seems that the proprietary software creates folders that are read-only by default. I need to change the file permissions for these folders in order for shutil.move() to delete the folders after they are copied to the network drive.

I have searched on SO to discover that os.chmod(path, 0o777) only works to grant access on Unix systems. On Windows, it modifies the read-only attribute of a file or folder. This question seems to yield a solution, which I tried as follows:

import win32security
import ntsecuritycon as con

account = r"admin"

userx, domain, type = win32security.LookupAccountName ("", account)
sd = win32security.GetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION)
dacl = sd.GetSecurityDescriptorDacl()   # instead of dacl = win32security.ACL()
dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.FILE_GENERIC_READ | con.FILE_GENERIC_WRITE, userx)
sd.SetSecurityDescriptorDacl(1, dacl, 0)   # may not be necessary
win32security.SetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION, sd)

But it does not seem to work. Also, I don't understand what I am doing with the modules win32security and ntsecuritycon. Maybe someone can give an easy explanation.

edit: ok so i looked at stuff. This is the exception that gets raised:

Traceback (most recent call last):
File "copyscript.py", line 108, in <module>
copyscript()# the loop needs to be called as a function to delete all assigned variables after each loop
File "copyscript.py", line 93, in copyscript
shutil.move(run, str(target_dir2))#move files renamed to user folder
File "C:\Users\admin\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 550, in move
rmtree(src)
File "C:\Users\admin\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 488, in rmtree
return _rmtree_unsafe(path, onerror)
File "C:\Users\admin\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 378, in _rmtree_unsafe
_rmtree_unsafe(fullname, onerror)
File "C:\Users\admin\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 383, in _rmtree_unsafe
onerror(os.unlink, fullname, sys.exc_info())
File "C:\Users\admin\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 381, in _rmtree_unsafe
os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'THG126.D\\AcqData\\sample_info.xml'
  • the full path of this file is D:\MSD_Data\THG126.D\AcqData\sample_info.xml.
  • the user account is named "admin" and it belongs to the "Administrators" group.
  • "admin" is the owner and has "full control" according to the "advanced security settings" for MSD_Data, THG126.D, sample_info.xml and the python script.
  • i have also tried running the script via CLI using "run as administrator". The same error occurs.

i looked at all files in the folders and found that only sample_info.xml has RA attributes, whereas all other have only A, so i added

path2 = r"D:\\MSD_data\\"+run+r"\\AcqData\\sample_info.xml"
subprocess.check_call(["attrib", "-r", path2, "/S", "/D"])

to the script and it seems to work now. I need to wait a little for new folders to be generated by the other software to see if the script is working correctly now.

Chris
  • 61
  • 1
  • 1
  • 7
  • 1
    There is no easy explanation of Windows security. It is complex. Until you can fully describe the action you wish to perform, who could you hope to code it? Trial and error isn't likely to succeed. – David Heffernan Aug 12 '17 at 07:53
  • I thought i 'fully described' the action. I am trying to `shutil.move` a folder to a network drive. I also gave a link to the full script. – Chris Aug 12 '17 at 07:55
  • The "read-only" attribute on Windows folders is meaningless. Before you start investing time in a problem you most likely do not even have, check the file and folder ACLs throughly and figure out exactly what permission setting prevents your script from deleting the files in question. – Tomalak Aug 12 '17 at 07:55
  • Not remotely close. You have not explained what action needs to be performed to allow the folder to be moved. What security settings must be changed. Since you don't understand Windows security yet you can't possibly know what needs to be done. You are trying to run before you can walk. – David Heffernan Aug 12 '17 at 07:57
  • @Chris You gave us nothing, really. `shutil.move` to a network drive is (like any file move operation across volume borders) a copy-and-delete operation. I take it that the "copy" part succeeds and the "delete" part fails because the account your script runs as does not have "delete" permissions on the folder in question. Figuring out why this is and fixing it (in the file system OR in the account hat runs your script) is the solution. It's highly unlikely that your script can give itself permissions it does not have, so trying to do that will turn out as a waste of time. – Tomalak Aug 12 '17 at 08:00
  • Ah ok! I will look into it and try to come back with more information or a solution. – Chris Aug 12 '17 at 08:26
  • 1
    @Tomalak, the read-only attribute on a folder is not generally meaningless to `shutil.move`. Removing the folder itself (not its contents) will fail if its read-only attribute is set. But the question is missing the traceback(s), doesn't show the folder attributes from attrib.exe (or `oct(stat.S_IMODE(os.stat(path).st_mode)`), and doesn't show the security from icacls.exe, so we don't have enough information to really help. – Eryk Sun Aug 12 '17 at 08:46
  • 1
    Also, `SetFileSecurity` is obsolete. Last April I added an updated answer to the linked question that uses `SetEntriesInAcl` and `SetNamedSecurityInfo`. – Eryk Sun Aug 12 '17 at 08:50
  • @eryksun A process who does not have the right to delete a file will with 99.9% certainty also not have the right to change that circumstance, no matter what API function it calls. As for the read-only attribute in folders, I A) doubt that it's really set (though I can imagine why the OP thinks it is) and B) [the documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365535(v=vs.85).aspx) clearly states *"This attribute is not honored on directories."* Your claim that `shutil.move` fails when it is set on a folder is false (try it). – Tomalak Aug 12 '17 at 09:18
  • 1
    @Tomalak, within a volume the `os.rename` call (i.e. WinAPI `MoveFile[Ex]`) will succeed because it's just a relink; the directory doesn't get deleted. For a cross-volume move it has to delete the directory, and if it's read-only this will fail. When the docs say that read-only isn't honored for directories, they mean that it doesn't prevent you from adding or removing files from the directory, not that it doesn't prevent removing the directory itself. – Eryk Sun Aug 12 '17 at 09:34
  • 1
    @Tomalak, without seeing the file security, we can't say whether or not the current access token grants the right to modify the DACL. If the user is the owner, or its owned by a group in the user's access token, or the user can take ownership via `SeTakeOwnershipPrivilege`, then we're not at a dead end. – Eryk Sun Aug 12 '17 at 09:39
  • *" For a cross-volume move it has to delete the directory, and if it's read-only this will fail."* No. Try it. – Tomalak Aug 12 '17 at 09:40
  • @Tomalak, there's something wrong with your test. I have an empty read-only directory named `C:\Temp\test`, and both `shutil.move('C:/Temp/test', 'E:/')` and `os.rmdir('C:/Temp/test')` fail with access denied. When I remove the read-only attribute it succeeds. – Eryk Sun Aug 12 '17 at 09:47
  • I did the same thing. Create folder. Check the "Read Only" flag via Explorer context menu. Call `shutil.move('C:\\test', 'D:\\')`. Works. It *must* work. What `shutil.move` does is call the Windows API function that moves a file. What `shutil.move` *doesn't* do is check the state of that flag prior to that - and Windows itself ignores this flag on directories. When you can move something with the cmd.exe `move` command or with drag&drop, you can move it with `shutil.move`. – Tomalak Aug 12 '17 at 09:52
  • 1
    @Tomalak, there's the problem. The read-only flag in Explorer explicitly tells you that it only applies to files in the directory. It doesn't set the read-only attribute on the directory. Use attrib.exe for that. – Eryk Sun Aug 12 '17 at 09:59
  • Ahh, right. Fair enough. Setting file attributes works by calling [`SetFileAttributes`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365535(v=vs.85).aspx) and has nothing to do with permissions, as opposed to what the OP was trying. Somehow my crystal ball still tells me that the OP actually has a permission issue. – Tomalak Aug 12 '17 at 10:08
  • it seems that [calling attrib.exe](https://docs.python.org/2/library/subprocess.html) and explicitly [setting the file attributes](https://www.howtogeek.com/205910/how-to-change-file-attributes-with-attrib-from-the-windows-command-prompt/) of `sample_info.xml` from RA to A solved the issue. While i understand that a read-only file cannot be deleted, i do not understand why all other approaches did not work. – Chris Aug 14 '17 at 12:45

1 Answers1

2

The problem seems to have been that a file had the attribute "RA", which means "read-only" and "archived". Even though the used user account wqas the owner of all files and folders, shutil.move() fails when it tries to delete the file after copying to the target location.

A workaround to this problem is to use

subprocess.check_call(["attrib", "-r", path])

to remove the read-only file attribute. This resolved my issue. If you still have trouble with shutil.move() you could also try this solution.

Chris
  • 61
  • 1
  • 1
  • 7