summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorowsla <owsla@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2008-07-02 18:03:40 +0000
committerowsla <owsla@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2008-07-02 18:03:40 +0000
commitfaf2b8f85c7d06021c5386c3aba0c3f16fb44a26 (patch)
tree30ae0d6de8e48916822aa5ffb1d85c67c01bb882
parentf840ffb6ea47b46d40057feea78dae1dbd30e511 (diff)
downloadrdiff-backup-faf2b8f85c7d06021c5386c3aba0c3f16fb44a26.tar.gz
Support for Windows ACLs. (Patch from Josh Nisly and Fred Gansevles)
git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@904 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
-rw-r--r--rdiff-backup/CHANGELOG2
-rwxr-xr-xrdiff-backup/dist/makedist2
-rw-r--r--rdiff-backup/rdiff_backup/Globals.py6
-rw-r--r--rdiff-backup/rdiff_backup/connection.py6
-rw-r--r--rdiff-backup/rdiff_backup/fs_abilities.py34
-rw-r--r--rdiff-backup/rdiff_backup/metadata.py37
-rw-r--r--rdiff-backup/rdiff_backup/rpath.py38
-rw-r--r--rdiff-backup/rdiff_backup/win_acls.py199
8 files changed, 313 insertions, 11 deletions
diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG
index f587cde..39c474f 100644
--- a/rdiff-backup/CHANGELOG
+++ b/rdiff-backup/CHANGELOG
@@ -1,6 +1,8 @@
New in v1.1.17 (????/??/??)
---------------------------
+Support for Windows ACLs. (Patch from Josh Nisly and Fred Gansevles)
+
Fix user_group.py to run on native Windows, which lacks grp and pwd Python
modules. (Patch from Fred Gansevles)
diff --git a/rdiff-backup/dist/makedist b/rdiff-backup/dist/makedist
index ff2953c..f50529e 100755
--- a/rdiff-backup/dist/makedist
+++ b/rdiff-backup/dist/makedist
@@ -118,7 +118,7 @@ def MakeTar(specfiles):
"Security.py", "selection.py",
"SetConnections.py", "static.py",
"statistics.py", "TempFile.py", "Time.py",
- "user_group.py"]:
+ "user_group.py", "win_acls.py"]:
shutil.copyfile(os.path.join(SourceDir, filename),
os.path.join(tardir, "rdiff_backup", filename))
diff --git a/rdiff-backup/rdiff_backup/Globals.py b/rdiff-backup/rdiff_backup/Globals.py
index 740127c..ffccc27 100644
--- a/rdiff-backup/rdiff_backup/Globals.py
+++ b/rdiff-backup/rdiff_backup/Globals.py
@@ -85,6 +85,12 @@ acls_active = None
acls_write = None
acls_conn = None
+# Like the above, but applies to support of Windows
+# access control lists.
+win_acls_active = None
+win_acls_write = None
+win_acls_conn = None
+
# Like above two setting groups, but applies to support of Mac OS X
# style resource forks.
resource_forks_active = None
diff --git a/rdiff-backup/rdiff_backup/connection.py b/rdiff-backup/rdiff_backup/connection.py
index 0ba1204..34aebae 100644
--- a/rdiff-backup/rdiff_backup/connection.py
+++ b/rdiff-backup/rdiff_backup/connection.py
@@ -27,7 +27,8 @@ try: import xattr
except ImportError: pass
try: import posix1e
except ImportError: pass
-
+try: import win32security
+except ImportError: pass
class ConnectionError(Exception): pass
class ConnectionReadError(ConnectionError): pass
@@ -539,6 +540,9 @@ import Globals, Time, Rdiff, Hardlink, FilenameMapping, C, Security, \
TempFile, SetConnections, librsync, log, regress, fs_abilities, \
eas_acls, user_group, compare
+try: import win_acls
+except ImportError: pass
+
Globals.local_connection = LocalConnection()
Globals.connections.append(Globals.local_connection)
# Following changed by server in SetConnections
diff --git a/rdiff-backup/rdiff_backup/fs_abilities.py b/rdiff-backup/rdiff_backup/fs_abilities.py
index 9d125b1..286731e 100644
--- a/rdiff-backup/rdiff_backup/fs_abilities.py
+++ b/rdiff-backup/rdiff_backup/fs_abilities.py
@@ -29,7 +29,7 @@ FSAbilities object describing it.
import errno, os
import Globals, log, TempFile, selection, robust, SetConnections, \
- static, FilenameMapping
+ static, FilenameMapping, win_acls
class FSAbilities:
"""Store capabilities of given file system"""
@@ -39,6 +39,7 @@ class FSAbilities:
ownership = None # True if chown works on this filesystem
acls = None # True if access control lists supported
eas = None # True if extended attributes supported
+ win_acls = None # True if windows access control lists supported
hardlinks = None # True if hard linking supported
fsync_dirs = None # True if directories can be fsync'd
dir_inc_perms = None # True if regular files can have full permissions
@@ -97,6 +98,7 @@ class FSAbilities:
self.win_reserved_filenames)])
add_boolean_list([('Access control lists', self.acls),
('Extended attributes', self.eas),
+ ('Windows access control lists', self.win_acls),
('Case sensitivity', self.case_sensitive),
('Escape DOS devices', self.escape_dos_devices),
('Mac OS X style resource forks',
@@ -120,6 +122,7 @@ class FSAbilities:
self.read_only = 1
self.set_eas(rp, 0)
self.set_acls(rp)
+ self.set_win_acls(rp)
self.set_resource_fork_readonly(rp)
self.set_carbonfile()
self.set_case_sensitive_readonly(rp)
@@ -151,6 +154,7 @@ class FSAbilities:
self.set_fsync_dirs(subdir)
self.set_eas(subdir, 1)
self.set_acls(subdir)
+ self.set_win_acls(subdir)
self.set_dir_inc_perms(subdir)
self.set_resource_fork_readwrite(subdir)
self.set_carbonfile()
@@ -364,6 +368,24 @@ class FSAbilities:
self.eas = 0
else: self.eas = 1
+ def set_win_acls(self, dir_rp):
+ """Test if windows access control lists are supported"""
+ try:
+ import win32security
+ except ImportError:
+ log.Log("Unable to import win32security module. Windows ACLs\n"
+ "not supported by filesystem at %s" % dir_rp.path, 4)
+ self.win_acls = 0
+ return
+ try:
+ win_acls.init_acls()
+ except OSError:
+ log.Log("Windows ACLs not supported by filesystem\n"
+ "at %s" % dir_rp.path, 4)
+ self.win_acls = 0
+ return
+ self.win_acls = 1
+
def set_dir_inc_perms(self, rp):
"""See if increments can have full permissions like a directory"""
test_rp = rp.append('dir_inc_check')
@@ -521,6 +543,10 @@ class SetGlobals:
log.Log.FatalError("--never-drop-acls specified, but ACL support\n"
"missing from destination filesystem")
+ def set_win_acls(self):
+ self.update_triple(self.src_fsa.win_acls, self.dest_fsa.win_acls,
+ ('win_acls_active', 'win_acls_write', 'win_acls_conn'))
+
def set_resource_forks(self):
self.update_triple(self.src_fsa.resource_forks,
self.dest_fsa.resource_forks,
@@ -729,6 +755,10 @@ class SingleSetGlobals(RestoreSetGlobals):
def set_acls(self):
self.update_triple(self.dest_fsa.acls,
('acls_active', 'acls_write', 'acls_conn'))
+ def set_win_acls(self):
+ self.update_triple(self.src_fsa.win_acls, self.dest_fsa.win_acls,
+ ('win_acls_active', 'win_acls_write', 'win_acls_conn'))
+
def set_resource_forks(self):
self.update_triple(self.dest_fsa.resource_forks,
('resource_forks_active',
@@ -754,6 +784,7 @@ def backup_set_globals(rpin, force):
bsg = BackupSetGlobals(rpin.conn, Globals.rbdir.conn, src_fsa, dest_fsa)
bsg.set_eas()
bsg.set_acls()
+ bsg.set_win_acls()
bsg.set_resource_forks()
bsg.set_carbonfile()
bsg.set_hardlinks()
@@ -781,6 +812,7 @@ def restore_set_globals(rpout):
rsg = RestoreSetGlobals(Globals.rbdir.conn, rpout.conn, src_fsa, dest_fsa)
rsg.set_eas()
rsg.set_acls()
+ rsg.set_win_acls()
rsg.set_resource_forks()
rsg.set_carbonfile()
rsg.set_hardlinks()
diff --git a/rdiff-backup/rdiff_backup/metadata.py b/rdiff-backup/rdiff_backup/metadata.py
index c20092b..1bbc5c6 100644
--- a/rdiff-backup/rdiff_backup/metadata.py
+++ b/rdiff-backup/rdiff_backup/metadata.py
@@ -433,9 +433,10 @@ class MetadataFile(FlatFile):
class CombinedWriter:
"""Used for simultaneously writting metadata, eas, and acls"""
- def __init__(self, metawriter, eawriter, aclwriter):
+ def __init__(self, metawriter, eawriter, aclwriter, winaclwriter):
self.metawriter = metawriter
- self.eawriter, self.aclwriter = eawriter, aclwriter # these can be None
+ self.eawriter, self.aclwriter, self.winaclwriter = \
+ eawriter, aclwriter, winaclwriter # these can be None
def write_object(self, rorp):
"""Write information in rorp to all the writers"""
@@ -444,11 +445,14 @@ class CombinedWriter:
self.eawriter.write_object(rorp.get_ea())
if self.aclwriter and not rorp.get_acl().is_basic():
self.aclwriter.write_object(rorp.get_acl())
+ if self.winaclwriter:
+ self.winaclwriter.write_object(rorp.get_win_acl())
def close(self):
self.metawriter.close()
if self.eawriter: self.eawriter.close()
if self.aclwriter: self.aclwriter.close()
+ if self.winaclwriter: self.winaclwriter.close()
class Manager:
@@ -456,6 +460,7 @@ class Manager:
meta_prefix = 'mirror_metadata'
acl_prefix = 'access_control_lists'
ea_prefix = 'extended_attributes'
+ wacl_prefix = 'win_access_control_lists'
def __init__(self):
"""Set listing of rdiff-backup-data dir"""
@@ -501,6 +506,11 @@ class Manager:
return self._iter_helper(self.acl_prefix,
eas_acls.AccessControlListFile, time, restrict_index)
+ def get_win_acls_at_time(self, time, restrict_index):
+ """Return WACLs iter at given time from recordfile (or None)"""
+ return self._iter_helper(self.wacl_prefix,
+ win_acls.WinAccessControlListFile, time, restrict_index)
+
def GetAtTime(self, time, restrict_index = None):
"""Return combined metadata iter with ea/acl info if necessary"""
cur_iter = self.get_meta_at_time(time, restrict_index)
@@ -521,6 +531,14 @@ class Manager:
log.Log("Warning: Extended Attributes file not found", 2)
ea_iter = iter([])
cur_iter = eas_acls.join_ea_iter(cur_iter, ea_iter)
+ if Globals.win_acls_active:
+ wacl_iter = self.get_win_acls_at_time(time, restrict_index)
+ if not wacl_iter:
+ log.Log("Warning: Windows Access Control List file not"
+ " found.", 2)
+ wacl_iter = iter([])
+ cur_iter = win_acls.join_wacl_iter(cur_iter, wacl_iter)
+
return cur_iter
def _writer_helper(self, prefix, flatfileclass, typestr, time):
@@ -548,17 +566,26 @@ class Manager:
return self._writer_helper(self.acl_prefix,
eas_acls.AccessControlListFile, typestr, time)
+ def get_win_acl_writer(self, typestr, time):
+ """Return WinAccessControlListFile opened for writing"""
+ return self._writer_helper(self.wacl_prefix,
+ win_acls.WinAccessControlListFile, typestr, time)
+
def GetWriter(self, typestr = 'snapshot', time = None):
"""Get a writer object that can write meta and possibly acls/eas"""
metawriter = self.get_meta_writer(typestr, time)
- if not Globals.eas_active and not Globals.acls_active:
+ if not Globals.eas_active and not Globals.acls_active and \
+ not Globals.win_acls_active:
return metawriter # no need for a CombinedWriter
if Globals.eas_active: ea_writer = self.get_ea_writer(typestr, time)
else: ea_writer = None
if Globals.acls_active: acl_writer = self.get_acl_writer(typestr, time)
else: acl_writer = None
- return CombinedWriter(metawriter, ea_writer, acl_writer)
+ if Globals.win_acls_active: win_acl_writer = \
+ self.get_win_acl_writer(typestr, time)
+ else: win_acl_writer = None
+ return CombinedWriter(metawriter, ea_writer, acl_writer, win_acl_writer)
class PatchDiffMan(Manager):
"""Contains functions for patching and diffing metadata
@@ -663,4 +690,4 @@ def SetManager():
return ManagerObj
-import eas_acls # put at bottom to avoid python circularity bug
+import eas_acls, win_acls # put at bottom to avoid python circularity bug
diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py
index 7cdaa8d..2acfc32 100644
--- a/rdiff-backup/rdiff_backup/rpath.py
+++ b/rdiff-backup/rdiff_backup/rpath.py
@@ -185,6 +185,7 @@ def copy_attribs(rpin, rpout):
rpout.chmod(rpin.getperms())
if Globals.acls_write: rpout.write_acl(rpin.get_acl())
if not rpin.isdev(): rpout.setmtime(rpin.getmtime())
+ if Globals.win_acls_write: rpout.write_win_acl(rpin.get_win_acl())
def copy_attribs_inc(rpin, rpout):
"""Change file attributes of rpout to match rpin
@@ -358,6 +359,7 @@ class RORPath:
elif key == 'size' and not self.isreg(): pass
elif key == 'ea' and not Globals.eas_active: pass
elif key == 'acl' and not Globals.acls_active: pass
+ elif key == 'win_acl' and not Globals.win_acls_active: pass
elif key == 'carbonfile' and not Globals.carbonfile_active: pass
elif key == 'resourcefork' and not Globals.resource_forks_active:
pass
@@ -398,6 +400,7 @@ class RORPath:
elif key == 'inode': pass
elif key == 'ea' and not Globals.eas_write: pass
elif key == 'acl' and not Globals.acls_write: pass
+ elif key == 'win_acl' and not Globals.win_acls_write: pass
elif key == 'carbonfile' and not Globals.carbonfile_write: pass
elif key == 'resourcefork' and not Globals.resource_forks_write:
pass
@@ -415,8 +418,8 @@ class RORPath:
def equal_verbose(self, other, check_index = 1,
compare_inodes = 0, compare_ownership = 0,
- compare_acls = 0, compare_eas = 0, compare_size = 1,
- compare_type = 1, verbosity = 2):
+ compare_acls = 0, compare_eas = 0, compare_win_acls = 0,
+ compare_size = 1, compare_type = 1, verbosity = 2):
"""Like __eq__, but log more information. Useful when testing"""
if check_index and self.index != other.index:
log.Log("Index %s != index %s" % (self.index, other.index),
@@ -437,6 +440,7 @@ class RORPath:
pass
elif key == 'ea' and not compare_eas: pass
elif key == 'acl' and not compare_acls: pass
+ elif key == 'win_acl' and not compare_win_acls: pass
elif (not other.data.has_key(key) or
self.data[key] != other.data[key]):
if not other.data.has_key(key):
@@ -454,7 +458,8 @@ class RORPath:
return self.equal_verbose(other,
compare_inodes = compare_inodes,
compare_eas = Globals.eas_active,
- compare_acls = Globals.acls_active)
+ compare_acls = Globals.acls_active,
+ compare_win_acls = Globals.win_acls_active)
def __ne__(self, other): return not self.__eq__(other)
@@ -702,6 +707,17 @@ class RORPath:
"""Record resource fork in dictionary. Does not write"""
self.data['resourcefork'] = rfork
+ def set_win_acl(self, acl):
+ """Record Windows access control list in dictionary. Does not write"""
+ self.data['win_acl'] = acl
+
+ def get_win_acl(self):
+ """Return access control list object from dictionary"""
+ try: return self.data['win_acl']
+ except KeyError:
+ acl = self.data['win_acl'] = get_blank_win_acl(self.index)
+ return acl
+
def has_alt_mirror_name(self):
"""True if rorp has an alternate mirror name specified"""
return self.data.has_key('mirrorname')
@@ -1316,6 +1332,16 @@ class RPath(RORPath):
assert not fp.close()
self.set_resource_fork(rfork_data)
+ def get_win_acl(self):
+ """Return Windows access control list, setting if necessary"""
+ try: acl = self.data['win_acl']
+ except KeyError: acl = self.data['win_acl'] = win_acl_get(self)
+ return acl
+
+ def write_win_acl(self, acl):
+ """Change access control list of rp"""
+ write_win_acl(self, acl)
+ self.data['win_acl'] = acl
class RPathFileHook:
"""Look like a file, but add closing hook"""
@@ -1406,6 +1432,8 @@ def setdata_local(rpath):
rpath.data['gname'] = user_group.gid2gname(rpath.data['gid'])
if Globals.eas_conn: rpath.data['ea'] = ea_get(rpath)
if Globals.acls_conn: rpath.data['acl'] = acl_get(rpath)
+ if Globals.win_acls_conn:
+ rpath.data['win_acl'] = win_acl_get(rpath)
if Globals.resource_forks_conn and rpath.isreg():
rpath.get_resource_fork()
if Globals.carbonfile_conn and rpath.isreg():
@@ -1439,3 +1467,7 @@ def acl_get(rp): assert 0
def get_blank_acl(index): assert 0
def ea_get(rp): assert 0
def get_blank_ea(index): assert 0
+
+def win_acl_get(rp): assert 0
+def write_win_acl(rp): assert 0
+def get_blank_win_acl(): assert 0
diff --git a/rdiff-backup/rdiff_backup/win_acls.py b/rdiff-backup/rdiff_backup/win_acls.py
new file mode 100644
index 0000000..1e95533
--- /dev/null
+++ b/rdiff-backup/rdiff_backup/win_acls.py
@@ -0,0 +1,199 @@
+# Copyright 2008 Fred Gansevles <fred@betterbe.com>
+#
+# This file is part of rdiff-backup.
+#
+# rdiff-backup is free software; you can redistribute it and/or modify
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# rdiff-backup is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with rdiff-backup; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import C, metadata, re, rorpiter, rpath
+
+try:
+ from win32security import *
+except:
+ GROUP_SECURITY_INFORMATION = 0
+ OWNER_SECURITY_INFORMATION = 0
+ DACL_SECURITY_INFORMATION = 0
+
+class ACL:
+ flags = (GROUP_SECURITY_INFORMATION|
+ OWNER_SECURITY_INFORMATION|
+ DACL_SECURITY_INFORMATION)
+
+ def __init__(self, index=()):
+ self.__acl = ""
+ self.index = index
+
+ def get_indexpath(self): return self.index and '/'.join(self.index) or '.'
+
+ def load_from_rp(self, rp, skip_inherit_only = True):
+ self.index = rp.index
+ try:
+ sd = rp.conn.win32security.GetFileSecurity(rp.path, ACL.flags)
+ except:
+ return
+
+ if skip_inherit_only:
+ # skip the inherit_only aces
+ acl = sd.GetSecurityDescriptorDacl()
+ if acl:
+ n = acl.GetAceCount()
+ # traverse the ACL in reverse, so the indices stay correct
+ while n:
+ n -= 1
+ ace_flags = acl.GetAce(n)[0][1]
+ if ace_flags & INHERIT_ONLY_ACE:
+ acl.DeleteAce(n)
+ sd.SetSecurityDescriptorDacl(1, acl, 0)
+
+ if ACL.flags & SACL_SECURITY_INFORMATION:
+ acl = sd.GetSecurityDescriptorSacl()
+ if acl:
+ n = acl.GetAceCount()
+ # traverse the ACL in reverse, so the indices stay correct
+ while n:
+ n -= 1
+ ace_flags = acl.GetAce(n)[0][1]
+ if ace_flags & INHERIT_ONLY_ACE:
+ acl.DeleteAce(n)
+ sd.SetSecurityDescriptorSacl(1, acl, 0)
+
+ self.__acl = \
+ rp.conn.win32security.ConvertSecurityDescriptorToStringSecurityDescriptor(sd,
+ SDDL_REVISION_1, ACL.flags)
+
+ def clear_rp(self, rp):
+ # not sure how to interpret this
+ # I'll jus clear all acl-s from rp.path
+ sd = rp.conn.win32security.GetFileSecurity(rp.path, ACL.flags)
+
+ acl = sd.GetSecurityDescriptorDacl()
+ if acl:
+ n = acl.GetAceCount()
+ # traverse the ACL in reverse, so the indices stay correct
+ while n:
+ n -= 1
+ acl.DeleteAce(n)
+ sd.SetSecurityDescriptorDacl(1, acl, 0)
+
+ if ACL.flags & SACL_SECURITY_INFORMATION:
+ acl = sd.GetSecurityDescriptorSacl()
+ if acl:
+ n = acl.GetAceCount()
+ # traverse the ACL in reverse, so the indices stay correct
+ while n:
+ n -= 1
+ acl.DeleteAce(n)
+ sd.SetSecurityDescriptorSacl(1, acl, 0)
+
+ SetFileSecurity(rp.path, ACL.flags, sd)
+
+ def write_to_rp(self, rp):
+ if self.__acl:
+ sd = rp.conn.win32security.ConvertStringSecurityDescriptorToSecurityDescriptor(self.__acl,
+ SDDL_REVISION_1)
+ rp.conn.win32security.SetFileSecurity(rp.path, ACL.flags, sd)
+
+ def __str__(self):
+ return '# file: %s\n%s\n' % \
+ (C.acl_quote(self.get_indexpath()), unicode(self.__acl))
+
+ def from_string(self, acl_str):
+ lines = acl_str.splitlines()
+ if len(lines) != 2 or not lines[0][:8] == "# file: ":
+ raise metadata.ParsingError("Bad record beginning: " + lines[0][:8])
+ filename = lines[0][8:]
+ if filename == '.': self.index = ()
+ else: self.index = tuple(C.acl_unquote(filename).split('/'))
+ self.__acl = lines[1]
+
+def Record2WACL(record):
+ acl = ACL()
+ acl.from_string(record)
+ return acl
+
+def WACL2Record(wacl):
+ return unicode(wacl)
+
+class WACLExtractor(metadata.FlatExtractor):
+ """Iterate ExtendedAttributes objects from the WACL information file"""
+ record_boundary_regexp = re.compile('(?:\\n|^)(# file: (.*?))\\n')
+ record_to_object = staticmethod(Record2WACL)
+ def filename_to_index(self, filename):
+ """Convert possibly quoted filename to index tuple"""
+ if filename == '.': return ()
+ else: return tuple(C.acl_unquote(filename).split('/'))
+
+class WinAccessControlListFile(metadata.FlatFile):
+ """Store/retrieve ACLs from extended_attributes file"""
+ _prefix = "win_access_control_lists"
+ _extractor = WACLExtractor
+ _object_to_record = staticmethod(WACL2Record)
+
+def join_wacl_iter(rorp_iter, wacl_iter):
+ """Update a rorp iter by adding the information from acl_iter"""
+ for rorp, wacl in rorpiter.CollateIterators(rorp_iter, wacl_iter):
+ assert rorp, "Missing rorp for index %s" % (wacl.index,)
+ if not wacl: wacl = ACL(rorp.index)
+ rorp.set_win_acl(unicode(wacl))
+ yield rorp
+
+def rpath_acl_win_get(rpath):
+ acl = ACL()
+ acl.load_from_rp(rpath)
+ return unicode(acl)
+rpath.win_acl_get = rpath_acl_win_get
+
+def rpath_get_blank_win_acl(index):
+ acl = ACL(index)
+ return unicode(acl)
+rpath.get_blank_win_acl = rpath_get_blank_win_acl
+
+def rpath_set_win_acl(rp, acl_str):
+ acl = ACL()
+ acl.from_string(acl_str)
+ acl.write_to_rp(rp)
+rpath.write_win_acl = rpath_set_win_acl
+
+def init_acls():
+ # A process that tries to read or write a SACL needs
+ # to have and enable the SE_SECURITY_NAME privilege.
+ # And inorder to backup/restore, the SE_BACKUP_NAME and
+ # SE_RESTORE_NAME privileges are needed.
+ import win32api
+ try:
+ hnd = OpenProcessToken(win32api.GetCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES| TOKEN_QUERY)
+ except win32api.error:
+ return
+ try:
+ try:
+ lpv = lambda priv: LookupPrivilegeValue(None, priv)
+ # enable the SE_*_NAME priveleges
+ SecurityName = lpv(SE_SECURITY_NAME)
+ AdjustTokenPrivileges(hnd, False, [
+ (SecurityName, SE_PRIVILEGE_ENABLED),
+ (lpv(SE_BACKUP_NAME), SE_PRIVILEGE_ENABLED),
+ (lpv(SE_RESTORE_NAME), SE_PRIVILEGE_ENABLED)
+ ])
+ except win32api.error:
+ return
+ for name, enabled in GetTokenInformation(hnd, TokenPrivileges):
+ if name == SecurityName and enabled:
+ # now we *may* access the SACL (sigh)
+ ACL.flags |= SACL_SECURITY_INFORMATION
+ break
+ finally:
+ win32api.CloseHandle(hnd)
+