1# Unix SMB/CIFS implementation. 2# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010 3# 4# 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17# 18 19from __future__ import print_function 20"""NT Acls.""" 21 22 23import os 24import tarfile 25import tempfile 26import shutil 27 28import samba.xattr_native 29import samba.xattr_tdb 30import samba.posix_eadb 31from samba.samba3 import param as s3param 32from samba.dcerpc import security, xattr, idmap 33from samba.ndr import ndr_pack, ndr_unpack 34from samba.samba3 import smbd 35from samba.samba3 import libsmb_samba_internal as libsmb 36from samba.logger import get_samba_logger 37from samba import NTSTATUSError 38from samba.auth_util import system_session_unix 39 40# don't include volumes 41SMB_FILE_ATTRIBUTE_FLAGS = libsmb.FILE_ATTRIBUTE_SYSTEM | \ 42 libsmb.FILE_ATTRIBUTE_DIRECTORY | \ 43 libsmb.FILE_ATTRIBUTE_ARCHIVE | \ 44 libsmb.FILE_ATTRIBUTE_HIDDEN 45 46 47SECURITY_SECINFO_FLAGS = security.SECINFO_OWNER | \ 48 security.SECINFO_GROUP | \ 49 security.SECINFO_DACL | \ 50 security.SECINFO_SACL 51 52 53# SEC_FLAG_SYSTEM_SECURITY is required otherwise get Access Denied 54SECURITY_SEC_FLAGS = security.SEC_FLAG_SYSTEM_SECURITY | \ 55 security.SEC_STD_READ_CONTROL 56 57 58class XattrBackendError(Exception): 59 """A generic xattr backend error.""" 60 61 62def checkset_backend(lp, backend, eadbfile): 63 '''return the path to the eadb, or None''' 64 if backend is None: 65 xattr_tdb = lp.get("xattr_tdb:file") 66 if xattr_tdb is not None: 67 return (samba.xattr_tdb, lp.get("xattr_tdb:file")) 68 posix_eadb = lp.get("posix:eadb") 69 if posix_eadb is not None: 70 return (samba.posix_eadb, lp.get("posix:eadb")) 71 return (None, None) 72 elif backend == "native": 73 return (None, None) 74 elif backend == "eadb": 75 if eadbfile is not None: 76 return (samba.posix_eadb, eadbfile) 77 else: 78 return (samba.posix_eadb, os.path.abspath(os.path.join(lp.get("private dir"), "eadb.tdb"))) 79 elif backend == "tdb": 80 if eadbfile is not None: 81 return (samba.xattr_tdb, eadbfile) 82 else: 83 state_dir = lp.get("state directory") 84 db_path = os.path.abspath(os.path.join(state_dir, "xattr.tdb")) 85 return (samba.xattr_tdb, db_path) 86 else: 87 raise XattrBackendError("Invalid xattr backend choice %s" % backend) 88 89 90def getdosinfo(lp, file): 91 try: 92 attribute = samba.xattr_native.wrap_getxattr(file, 93 xattr.XATTR_DOSATTRIB_NAME_S3) 94 except Exception: 95 return 96 97 return ndr_unpack(xattr.DOSATTRIB, attribute) 98 99 100def getntacl(lp, 101 file, 102 session_info, 103 backend=None, 104 eadbfile=None, 105 direct_db_access=True, 106 service=None): 107 if direct_db_access: 108 (backend_obj, dbname) = checkset_backend(lp, backend, eadbfile) 109 if dbname is not None: 110 try: 111 attribute = backend_obj.wrap_getxattr(dbname, file, 112 xattr.XATTR_NTACL_NAME) 113 except Exception: 114 # FIXME: Don't catch all exceptions, just those related to opening 115 # xattrdb 116 print("Fail to open %s" % dbname) 117 attribute = samba.xattr_native.wrap_getxattr(file, 118 xattr.XATTR_NTACL_NAME) 119 else: 120 attribute = samba.xattr_native.wrap_getxattr(file, 121 xattr.XATTR_NTACL_NAME) 122 ntacl = ndr_unpack(xattr.NTACL, attribute) 123 if ntacl.version == 1: 124 return ntacl.info 125 elif ntacl.version == 2: 126 return ntacl.info.sd 127 elif ntacl.version == 3: 128 return ntacl.info.sd 129 elif ntacl.version == 4: 130 return ntacl.info.sd 131 else: 132 return smbd.get_nt_acl(file, 133 SECURITY_SECINFO_FLAGS, 134 session_info, 135 service=service) 136 137 138def setntacl(lp, file, sddl, domsid, session_info, 139 backend=None, eadbfile=None, 140 use_ntvfs=True, skip_invalid_chown=False, 141 passdb=None, service=None): 142 """ 143 A wrapper for smbd set_nt_acl api. 144 145 Args: 146 lp (LoadParam): load param from conf 147 file (str): a path to file or dir 148 sddl (str): ntacl sddl string 149 service (str): name of share service, e.g.: sysvol 150 session_info (auth_session_info): session info for authentication 151 152 Note: 153 Get `session_info` with `samba.auth.user_session`, do not use the 154 `admin_session` api. 155 156 Returns: 157 None 158 """ 159 160 assert(isinstance(domsid, str) or isinstance(domsid, security.dom_sid)) 161 if isinstance(domsid, str): 162 sid = security.dom_sid(domsid) 163 elif isinstance(domsid, security.dom_sid): 164 sid = domsid 165 domsid = str(sid) 166 167 assert(isinstance(sddl, str) or isinstance(sddl, security.descriptor)) 168 if isinstance(sddl, str): 169 sd = security.descriptor.from_sddl(sddl, sid) 170 elif isinstance(sddl, security.descriptor): 171 sd = sddl 172 sddl = sd.as_sddl(sid) 173 174 if not use_ntvfs and skip_invalid_chown: 175 # Check if the owner can be resolved as a UID 176 (owner_id, owner_type) = passdb.sid_to_id(sd.owner_sid) 177 if ((owner_type != idmap.ID_TYPE_UID) and (owner_type != idmap.ID_TYPE_BOTH)): 178 # Check if this particular owner SID was domain admins, 179 # because we special-case this as mapping to 180 # 'administrator' instead. 181 if sd.owner_sid == security.dom_sid("%s-%d" % (domsid, security.DOMAIN_RID_ADMINS)): 182 administrator = security.dom_sid("%s-%d" % (domsid, security.DOMAIN_RID_ADMINISTRATOR)) 183 (admin_id, admin_type) = passdb.sid_to_id(administrator) 184 185 # Confirm we have a UID for administrator 186 if ((admin_type == idmap.ID_TYPE_UID) or (admin_type == idmap.ID_TYPE_BOTH)): 187 188 # Set it, changing the owner to 'administrator' rather than domain admins 189 sd2 = sd 190 sd2.owner_sid = administrator 191 192 smbd.set_nt_acl( 193 file, SECURITY_SECINFO_FLAGS, sd2, 194 session_info, 195 service=service) 196 197 # and then set an NTVFS ACL (which does not set the posix ACL) to pretend the owner really was set 198 use_ntvfs = True 199 else: 200 raise XattrBackendError("Unable to find UID for domain administrator %s, got id %d of type %d" % (administrator, admin_id, admin_type)) 201 else: 202 # For all other owning users, reset the owner to root 203 # and then set the ACL without changing the owner 204 # 205 # This won't work in test environments, as it tries a real (rather than xattr-based fake) chown 206 207 os.chown(file, 0, 0) 208 smbd.set_nt_acl( 209 file, 210 security.SECINFO_GROUP | 211 security.SECINFO_DACL | 212 security.SECINFO_SACL, 213 sd, 214 session_info, 215 service=service) 216 217 if use_ntvfs: 218 (backend_obj, dbname) = checkset_backend(lp, backend, eadbfile) 219 ntacl = xattr.NTACL() 220 ntacl.version = 1 221 ntacl.info = sd 222 if dbname is not None: 223 try: 224 backend_obj.wrap_setxattr(dbname, 225 file, xattr.XATTR_NTACL_NAME, ndr_pack(ntacl)) 226 except Exception: 227 # FIXME: Don't catch all exceptions, just those related to opening 228 # xattrdb 229 print("Fail to open %s" % dbname) 230 samba.xattr_native.wrap_setxattr(file, xattr.XATTR_NTACL_NAME, 231 ndr_pack(ntacl)) 232 else: 233 samba.xattr_native.wrap_setxattr(file, xattr.XATTR_NTACL_NAME, 234 ndr_pack(ntacl)) 235 else: 236 smbd.set_nt_acl( 237 file, SECURITY_SECINFO_FLAGS, sd, 238 service=service, session_info=session_info) 239 240 241def ldapmask2filemask(ldm): 242 """Takes the access mask of a DS ACE and transform them in a File ACE mask. 243 """ 244 RIGHT_DS_CREATE_CHILD = 0x00000001 245 RIGHT_DS_DELETE_CHILD = 0x00000002 246 RIGHT_DS_LIST_CONTENTS = 0x00000004 247 ACTRL_DS_SELF = 0x00000008 248 RIGHT_DS_READ_PROPERTY = 0x00000010 249 RIGHT_DS_WRITE_PROPERTY = 0x00000020 250 RIGHT_DS_DELETE_TREE = 0x00000040 251 RIGHT_DS_LIST_OBJECT = 0x00000080 252 RIGHT_DS_CONTROL_ACCESS = 0x00000100 253 FILE_READ_DATA = 0x0001 254 FILE_LIST_DIRECTORY = 0x0001 255 FILE_WRITE_DATA = 0x0002 256 FILE_ADD_FILE = 0x0002 257 FILE_APPEND_DATA = 0x0004 258 FILE_ADD_SUBDIRECTORY = 0x0004 259 FILE_CREATE_PIPE_INSTANCE = 0x0004 260 FILE_READ_EA = 0x0008 261 FILE_WRITE_EA = 0x0010 262 FILE_EXECUTE = 0x0020 263 FILE_TRAVERSE = 0x0020 264 FILE_DELETE_CHILD = 0x0040 265 FILE_READ_ATTRIBUTES = 0x0080 266 FILE_WRITE_ATTRIBUTES = 0x0100 267 DELETE = 0x00010000 268 READ_CONTROL = 0x00020000 269 WRITE_DAC = 0x00040000 270 WRITE_OWNER = 0x00080000 271 SYNCHRONIZE = 0x00100000 272 STANDARD_RIGHTS_ALL = 0x001F0000 273 274 filemask = ldm & STANDARD_RIGHTS_ALL 275 276 if (ldm & RIGHT_DS_READ_PROPERTY) and (ldm & RIGHT_DS_LIST_CONTENTS): 277 filemask = filemask | (SYNCHRONIZE | FILE_LIST_DIRECTORY | 278 FILE_READ_ATTRIBUTES | FILE_READ_EA | 279 FILE_READ_DATA | FILE_EXECUTE) 280 281 if ldm & RIGHT_DS_WRITE_PROPERTY: 282 filemask = filemask | (SYNCHRONIZE | FILE_WRITE_DATA | 283 FILE_APPEND_DATA | FILE_WRITE_EA | 284 FILE_WRITE_ATTRIBUTES | FILE_ADD_FILE | 285 FILE_ADD_SUBDIRECTORY) 286 287 if ldm & RIGHT_DS_CREATE_CHILD: 288 filemask = filemask | (FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE) 289 290 if ldm & RIGHT_DS_DELETE_CHILD: 291 filemask = filemask | FILE_DELETE_CHILD 292 293 return filemask 294 295 296def dsacl2fsacl(dssddl, sid, as_sddl=True): 297 """ 298 299 This function takes an the SDDL representation of a DS 300 ACL and return the SDDL representation of this ACL adapted 301 for files. It's used for Policy object provision 302 """ 303 ref = security.descriptor.from_sddl(dssddl, sid) 304 fdescr = security.descriptor() 305 fdescr.owner_sid = ref.owner_sid 306 fdescr.group_sid = ref.group_sid 307 fdescr.type = ref.type 308 fdescr.revision = ref.revision 309 aces = ref.dacl.aces 310 for i in range(0, len(aces)): 311 ace = aces[i] 312 if not ace.type & security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT and str(ace.trustee) != security.SID_BUILTIN_PREW2K: 313 # if fdescr.type & security.SEC_DESC_DACL_AUTO_INHERITED: 314 ace.flags = ace.flags | security.SEC_ACE_FLAG_OBJECT_INHERIT | security.SEC_ACE_FLAG_CONTAINER_INHERIT 315 if str(ace.trustee) == security.SID_CREATOR_OWNER: 316 # For Creator/Owner the IO flag is set as this ACE has only a sense for child objects 317 ace.flags = ace.flags | security.SEC_ACE_FLAG_INHERIT_ONLY 318 ace.access_mask = ldapmask2filemask(ace.access_mask) 319 fdescr.dacl_add(ace) 320 321 if not as_sddl: 322 return fdescr 323 324 return fdescr.as_sddl(sid) 325 326 327class SMBHelper: 328 """ 329 A wrapper class for SMB connection 330 331 smb_path: path with separator "\\" other than "/" 332 """ 333 334 def __init__(self, smb_conn, dom_sid): 335 self.smb_conn = smb_conn 336 self.dom_sid = dom_sid 337 338 def get_acl(self, smb_path, as_sddl=False): 339 assert '/' not in smb_path 340 341 ntacl_sd = self.smb_conn.get_acl( 342 smb_path, SECURITY_SECINFO_FLAGS, SECURITY_SEC_FLAGS) 343 344 return ntacl_sd.as_sddl(self.dom_sid) if as_sddl else ntacl_sd 345 346 def list(self, smb_path=''): 347 """ 348 List file and dir base names in smb_path without recursive. 349 """ 350 assert '/' not in smb_path 351 return self.smb_conn.list(smb_path, attribs=SMB_FILE_ATTRIBUTE_FLAGS) 352 353 def is_dir(self, attrib): 354 """ 355 Check whether the attrib value is a directory. 356 357 attrib is from list method. 358 """ 359 return bool(attrib & libsmb.FILE_ATTRIBUTE_DIRECTORY) 360 361 def join(self, root, name): 362 """ 363 Join path with '\\' 364 """ 365 return root + '\\' + name if root else name 366 367 def loadfile(self, smb_path): 368 assert '/' not in smb_path 369 return self.smb_conn.loadfile(smb_path) 370 371 def create_tree(self, tree, smb_path=''): 372 """ 373 Create files as defined in tree 374 """ 375 for name, content in tree.items(): 376 fullname = self.join(smb_path, name) 377 if isinstance(content, dict): # a dir 378 if not self.smb_conn.chkpath(fullname): 379 self.smb_conn.mkdir(fullname) 380 self.create_tree(content, smb_path=fullname) 381 else: # a file 382 self.smb_conn.savefile(fullname, content) 383 384 def get_tree(self, smb_path=''): 385 """ 386 Get the tree structure via smb conn 387 388 self.smb_conn.list example: 389 390 [ 391 { 392 'attrib': 16, 393 'mtime': 1528848309, 394 'name': 'dir1', 395 'short_name': 'dir1', 396 'size': 0L 397 }, { 398 'attrib': 32, 399 'mtime': 1528848309, 400 'name': 'file0.txt', 401 'short_name': 'file0.txt', 402 'size': 10L 403 } 404 ] 405 """ 406 tree = {} 407 for item in self.list(smb_path): 408 name = item['name'] 409 fullname = self.join(smb_path, name) 410 if self.is_dir(item['attrib']): 411 tree[name] = self.get_tree(smb_path=fullname) 412 else: 413 tree[name] = self.loadfile(fullname) 414 return tree 415 416 def get_ntacls(self, smb_path=''): 417 """ 418 Get ntacl for each file and dir via smb conn 419 """ 420 ntacls = {} 421 for item in self.list(smb_path): 422 name = item['name'] 423 fullname = self.join(smb_path, name) 424 if self.is_dir(item['attrib']): 425 ntacls.update(self.get_ntacls(smb_path=fullname)) 426 else: 427 ntacl_sd = self.get_acl(fullname) 428 ntacls[fullname] = ntacl_sd.as_sddl(self.dom_sid) 429 return ntacls 430 431 def delete_tree(self): 432 for item in self.list(): 433 name = item['name'] 434 if self.is_dir(item['attrib']): 435 self.smb_conn.deltree(name) 436 else: 437 self.smb_conn.unlink(name) 438 439 440class NtaclsHelper: 441 442 def __init__(self, service, smb_conf_path, dom_sid): 443 self.service = service 444 self.dom_sid = dom_sid 445 446 # this is important to help smbd find services. 447 self.lp = s3param.get_context() 448 self.lp.load(smb_conf_path) 449 450 self.use_ntvfs = "smb" in self.lp.get("server services") 451 452 def getntacl(self, path, session_info, as_sddl=False, direct_db_access=None): 453 if direct_db_access is None: 454 direct_db_access = self.use_ntvfs 455 456 ntacl_sd = getntacl( 457 self.lp, path, session_info, 458 direct_db_access=direct_db_access, 459 service=self.service) 460 461 return ntacl_sd.as_sddl(self.dom_sid) if as_sddl else ntacl_sd 462 463 def setntacl(self, path, ntacl_sd, session_info): 464 # ntacl_sd can be obj or str 465 return setntacl(self.lp, path, ntacl_sd, self.dom_sid, session_info, 466 use_ntvfs=self.use_ntvfs) 467 468 469def _create_ntacl_file(dst, ntacl_sddl_str): 470 with open(dst + '.NTACL', 'w') as f: 471 f.write(ntacl_sddl_str) 472 473 474def _read_ntacl_file(src): 475 ntacl_file = src + '.NTACL' 476 477 if not os.path.exists(ntacl_file): 478 return None 479 480 with open(ntacl_file, 'r') as f: 481 return f.read() 482 483 484def backup_online(smb_conn, dest_tarfile_path, dom_sid): 485 """ 486 Backup all files and dirs with ntacl for the serive behind smb_conn. 487 488 1. Create a temp dir as container dir 489 2. Backup all files with dir structure into container dir 490 3. Generate file.NTACL files for each file and dir in contianer dir 491 4. Create a tar file from container dir(without top level folder) 492 5. Delete contianer dir 493 """ 494 495 logger = get_samba_logger() 496 497 if isinstance(dom_sid, str): 498 dom_sid = security.dom_sid(dom_sid) 499 500 smb_helper = SMBHelper(smb_conn, dom_sid) 501 502 remotedir = '' # root dir 503 504 localdir = tempfile.mkdtemp() 505 506 r_dirs = [remotedir] 507 l_dirs = [localdir] 508 509 while r_dirs: 510 r_dir = r_dirs.pop() 511 l_dir = l_dirs.pop() 512 513 for e in smb_helper.list(smb_path=r_dir): 514 r_name = smb_helper.join(r_dir, e['name']) 515 l_name = os.path.join(l_dir, e['name']) 516 517 if smb_helper.is_dir(e['attrib']): 518 r_dirs.append(r_name) 519 l_dirs.append(l_name) 520 os.mkdir(l_name) 521 else: 522 data = smb_helper.loadfile(r_name) 523 with open(l_name, 'wb') as f: 524 f.write(data) 525 526 # get ntacl for this entry and save alongside 527 try: 528 ntacl_sddl_str = smb_helper.get_acl(r_name, as_sddl=True) 529 _create_ntacl_file(l_name, ntacl_sddl_str) 530 except NTSTATUSError as e: 531 logger.error('Failed to get the ntacl for %s: %s' % \ 532 (r_name, e.args[1])) 533 logger.warning('The permissions for %s may not be' % r_name + 534 ' restored correctly') 535 536 with tarfile.open(name=dest_tarfile_path, mode='w:gz') as tar: 537 for name in os.listdir(localdir): 538 path = os.path.join(localdir, name) 539 tar.add(path, arcname=name) 540 541 shutil.rmtree(localdir) 542 543 544def backup_offline(src_service_path, dest_tarfile_path, samdb_conn, smb_conf_path): 545 """ 546 Backup files and ntacls to a tarfile for a service 547 """ 548 service = src_service_path.rstrip('/').rsplit('/', 1)[-1] 549 tempdir = tempfile.mkdtemp() 550 session_info = system_session_unix() 551 552 dom_sid_str = samdb_conn.get_domain_sid() 553 dom_sid = security.dom_sid(dom_sid_str) 554 555 ntacls_helper = NtaclsHelper(service, smb_conf_path, dom_sid) 556 557 for dirpath, dirnames, filenames in os.walk(src_service_path): 558 # each dir only cares about its direct children 559 rel_dirpath = os.path.relpath(dirpath, start=src_service_path) 560 dst_dirpath = os.path.join(tempdir, rel_dirpath) 561 562 # create sub dirs and NTACL file 563 for dirname in dirnames: 564 src = os.path.join(dirpath, dirname) 565 dst = os.path.join(dst_dirpath, dirname) 566 # mkdir with metadata 567 smbd.mkdir(dst, session_info, service) 568 ntacl_sddl_str = ntacls_helper.getntacl(src, session_info, as_sddl=True) 569 _create_ntacl_file(dst, ntacl_sddl_str) 570 571 # create files and NTACL file, then copy data 572 for filename in filenames: 573 src = os.path.join(dirpath, filename) 574 dst = os.path.join(dst_dirpath, filename) 575 # create an empty file with metadata 576 smbd.create_file(dst, session_info, service) 577 ntacl_sddl_str = ntacls_helper.getntacl(src, session_info, as_sddl=True) 578 _create_ntacl_file(dst, ntacl_sddl_str) 579 580 # now put data in 581 with open(src, 'rb') as src_file: 582 data = src_file.read() 583 with open(dst, 'wb') as dst_file: 584 dst_file.write(data) 585 586 # add all files in tempdir to tarfile without a top folder 587 with tarfile.open(name=dest_tarfile_path, mode='w:gz') as tar: 588 for name in os.listdir(tempdir): 589 path = os.path.join(tempdir, name) 590 tar.add(path, arcname=name) 591 592 shutil.rmtree(tempdir) 593 594 595def backup_restore(src_tarfile_path, dst_service_path, samdb_conn, smb_conf_path): 596 """ 597 Restore files and ntacls from a tarfile to a service 598 """ 599 logger = get_samba_logger() 600 service = dst_service_path.rstrip('/').rsplit('/', 1)[-1] 601 tempdir = tempfile.mkdtemp() # src files 602 603 dom_sid_str = samdb_conn.get_domain_sid() 604 dom_sid = security.dom_sid(dom_sid_str) 605 606 ntacls_helper = NtaclsHelper(service, smb_conf_path, dom_sid) 607 session_info = system_session_unix() 608 609 with tarfile.open(src_tarfile_path) as f: 610 f.extractall(path=tempdir) 611 # e.g.: /tmp/tmpRNystY/{dir1,dir1.NTACL,...file1,file1.NTACL} 612 613 for dirpath, dirnames, filenames in os.walk(tempdir): 614 rel_dirpath = os.path.relpath(dirpath, start=tempdir) 615 dst_dirpath = os.path.normpath( 616 os.path.join(dst_service_path, rel_dirpath)) 617 618 for dirname in dirnames: 619 if not dirname.endswith('.NTACL'): 620 src = os.path.join(dirpath, dirname) 621 dst = os.path.join(dst_dirpath, dirname) 622 if not os.path.isdir(dst): 623 # dst must be absolute path for smbd API 624 smbd.mkdir(dst, session_info, service) 625 626 ntacl_sddl_str = _read_ntacl_file(src) 627 if ntacl_sddl_str: 628 ntacls_helper.setntacl(dst, ntacl_sddl_str, session_info) 629 else: 630 logger.warning( 631 'Failed to restore ntacl for directory %s.' % dst 632 + ' Please check the permissions are correct') 633 634 for filename in filenames: 635 if not filename.endswith('.NTACL'): 636 src = os.path.join(dirpath, filename) 637 dst = os.path.join(dst_dirpath, filename) 638 if not os.path.isfile(dst): 639 # dst must be absolute path for smbd API 640 smbd.create_file(dst, session_info, service) 641 642 ntacl_sddl_str = _read_ntacl_file(src) 643 if ntacl_sddl_str: 644 ntacls_helper.setntacl(dst, ntacl_sddl_str, session_info) 645 else: 646 logger.warning('Failed to restore ntacl for file %s.' % dst 647 + ' Please check the permissions are correct') 648 649 # now put data in 650 with open(src, 'rb') as src_file: 651 data = src_file.read() 652 with open(dst, 'wb') as dst_file: 653 dst_file.write(data) 654 655 shutil.rmtree(tempdir) 656