mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-09 00:32:37 -04:00
Merge pull request #1037 from Anakonda/windows
Windows file permissions
This commit is contained in:
commit
ed4d2965cf
7 changed files with 374 additions and 13 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,6 +11,7 @@ crypto.c
|
|||
platform_darwin.c
|
||||
platform_freebsd.c
|
||||
platform_linux.c
|
||||
platform_windows.c
|
||||
*.egg-info
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ from .helpers import Chunk, Error, uid2user, user2uid, gid2group, group2gid, \
|
|||
CompressionDecider1, CompressionDecider2, CompressionSpec
|
||||
from .repository import Repository
|
||||
from .platform import acl_get, acl_set
|
||||
if sys.platform == 'win32':
|
||||
from .platform import get_owner, set_owner
|
||||
from .chunker import Chunker
|
||||
from .hashindex import ChunkIndex, ChunkIndexEntry
|
||||
from .cache import ChunkListEntry
|
||||
|
|
@ -423,6 +425,11 @@ Number of files: {0.stats.nfiles}'''.format(
|
|||
os.lchown(path, uid, gid)
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
set_owner(path, item[b'user'], safe_decode(item[b'uid']))
|
||||
except OSError:
|
||||
pass
|
||||
if sys.platform != 'win32':
|
||||
if fd:
|
||||
os.fchmod(fd, item[b'mode'])
|
||||
|
|
@ -501,14 +508,26 @@ Number of files: {0.stats.nfiles}'''.format(
|
|||
del self.manifest.archives[self.name]
|
||||
|
||||
def stat_attrs(self, st, path):
|
||||
item = {
|
||||
b'mode': st.st_mode,
|
||||
b'uid': st.st_uid, b'user': uid2user(st.st_uid),
|
||||
b'gid': st.st_gid, b'group': gid2group(st.st_gid),
|
||||
b'atime': int_to_bigint(st.st_atime_ns),
|
||||
b'ctime': int_to_bigint(st.st_ctime_ns),
|
||||
b'mtime': int_to_bigint(st.st_mtime_ns),
|
||||
}
|
||||
item = {}
|
||||
if sys.platform == 'win32':
|
||||
owner = get_owner(path)
|
||||
item = {
|
||||
b'mode': st.st_mode,
|
||||
b'uid': owner[1], b'user': owner[0],
|
||||
b'gid': st.st_gid, b'group': gid2group(st.st_gid),
|
||||
b'atime': int_to_bigint(st.st_atime_ns),
|
||||
b'ctime': int_to_bigint(st.st_ctime_ns),
|
||||
b'mtime': int_to_bigint(st.st_mtime_ns),
|
||||
}
|
||||
else:
|
||||
item = {
|
||||
b'mode': st.st_mode,
|
||||
b'uid': st.st_uid, b'user': uid2user(st.st_uid),
|
||||
b'gid': st.st_gid, b'group': gid2group(st.st_gid),
|
||||
b'atime': int_to_bigint(st.st_atime_ns),
|
||||
b'ctime': int_to_bigint(st.st_ctime_ns),
|
||||
b'mtime': int_to_bigint(st.st_mtime_ns),
|
||||
}
|
||||
if self.numeric_owner:
|
||||
item[b'user'] = item[b'group'] = None
|
||||
xattrs = xattr.get_all(path, follow_symlinks=False)
|
||||
|
|
|
|||
|
|
@ -735,7 +735,10 @@ class Archiver:
|
|||
elif args.short:
|
||||
format = "{path}{NL}"
|
||||
else:
|
||||
format = "{mode} {user:6} {group:6} {size:8} {isomtime} {path}{extra}{NL}"
|
||||
if sys.platform == 'win32':
|
||||
format = "{user:15} {size:8} {isomtime} {path}{extra}{NL}"
|
||||
else:
|
||||
format = "{mode} {user:6} {group:6} {size:8} {isomtime} {path}{extra}{NL}"
|
||||
formatter = ItemFormatter(archive, format)
|
||||
|
||||
if not hasattr(sys.stdout, 'buffer'):
|
||||
|
|
@ -2009,7 +2012,8 @@ class Archiver:
|
|||
|
||||
def prerun_checks(self, logger):
|
||||
check_extension_modules()
|
||||
selftest(logger)
|
||||
if sys.platform != 'win32':
|
||||
selftest(logger)
|
||||
|
||||
def run(self, args):
|
||||
os.umask(args.umask) # early, before opening files
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# this set must be kept complete, otherwise the RobustUnpacker might malfunction:
|
||||
ITEM_KEYS = set([b'path', b'source', b'rdev', b'chunks', b'hardlink_master',
|
||||
b'mode', b'user', b'group', b'uid', b'gid', b'mtime', b'atime', b'ctime',
|
||||
b'xattrs', b'bsdflags', b'acl_nfs4', b'acl_access', b'acl_default', b'acl_extended', ])
|
||||
b'xattrs', b'bsdflags', b'acl_nfs4', b'acl_access', b'acl_default', b'acl_extended', b'win_dacl'])
|
||||
|
||||
ARCHIVE_TEXT_KEYS = (b'name', b'comment', b'hostname', b'username', b'time', b'time_end')
|
||||
ITEM_TEXT_KEYS = (b'path', b'source', b'user', b'group')
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ elif sys.platform.startswith('freebsd'): # pragma: freebsd only
|
|||
from .platform_freebsd import acl_get, acl_set, API_VERSION
|
||||
elif sys.platform == 'darwin': # pragma: darwin only
|
||||
from .platform_darwin import acl_get, acl_set, API_VERSION
|
||||
elif sys.platform == 'win32': # pragma: windows only
|
||||
from .platform_windows import acl_get, acl_set, API_VERSION, get_owner, set_owner
|
||||
else: # pragma: unknown platform only
|
||||
API_VERSION = 2
|
||||
|
||||
|
|
|
|||
329
borg/platform_windows.pyx
Normal file
329
borg/platform_windows.pyx
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
#cython: language_level=3
|
||||
|
||||
import json
|
||||
from libc.stddef cimport wchar_t
|
||||
from libc.stdint cimport uint16_t, uint32_t, uint64_t
|
||||
cimport cpython.array
|
||||
import array
|
||||
|
||||
import platform
|
||||
from .helpers import safe_decode, safe_encode
|
||||
|
||||
API_VERSION = 2
|
||||
|
||||
|
||||
cdef extern from 'stdlib.h':
|
||||
void free(void* ptr)
|
||||
void* malloc(size_t)
|
||||
void* calloc(size_t, size_t)
|
||||
|
||||
|
||||
cdef extern from 'Python.h':
|
||||
wchar_t* PyUnicode_AsWideCharString(object, Py_ssize_t *)
|
||||
object PyUnicode_FromWideChar(const wchar_t*, Py_ssize_t)
|
||||
void* PyMem_Malloc(int)
|
||||
void PyMem_Free(void*)
|
||||
|
||||
|
||||
cdef extern from 'windows.h':
|
||||
ctypedef int HLOCAL
|
||||
ctypedef wchar_t* LPCTSTR
|
||||
ctypedef char BYTE
|
||||
ctypedef int HLOCAL
|
||||
ctypedef uint32_t DWORD
|
||||
ctypedef DWORD* LPDWORD
|
||||
ctypedef int BOOL
|
||||
ctypedef BYTE* PSID
|
||||
struct _ACL:
|
||||
uint16_t AceCount
|
||||
|
||||
HLOCAL LocalFree(HLOCAL)
|
||||
DWORD GetLastError();
|
||||
void SetLastError(DWORD)
|
||||
|
||||
DWORD FormatMessageW(DWORD, void*, DWORD, DWORD, wchar_t**, DWORD, void*)
|
||||
|
||||
|
||||
BOOL InitializeSecurityDescriptor(BYTE*, DWORD)
|
||||
|
||||
BOOL LookupAccountNameW(LPCTSTR, LPCTSTR, PSID, LPDWORD, LPCTSTR, LPDWORD, LPDWORD)
|
||||
BOOL GetSecurityDescriptorDacl(PSID, BOOL*, _ACL**, BOOL*)
|
||||
|
||||
cdef extern int ERROR_INSUFFICIENT_BUFFER
|
||||
cdef extern int ERROR_INVALID_SID
|
||||
cdef extern int ERROR_NONE_MAPPED
|
||||
|
||||
cdef extern int OWNER_SECURITY_INFORMATION
|
||||
cdef extern int GROUP_SECURITY_INFORMATION
|
||||
cdef extern int DACL_SECURITY_INFORMATION
|
||||
cdef extern int SACL_SECURITY_INFORMATION
|
||||
cdef extern int LABEL_SECURITY_INFORMATION
|
||||
cdef extern int ATTRIBUTE_SECURITY_INFORMATION
|
||||
cdef extern int SCOPE_SECURITY_INFORMATION
|
||||
cdef extern int BACKUP_SECURITY_INFORMATION
|
||||
cdef extern int UNPROTECTED_SACL_SECURITY_INFORMATION
|
||||
cdef extern int UNPROTECTED_DACL_SECURITY_INFORMATION
|
||||
cdef extern int PROTECTED_SACL_SECURITY_INFORMATION
|
||||
cdef extern int PROTECTED_DACL_SECURITY_INFORMATION
|
||||
|
||||
cdef extern int SECURITY_DESCRIPTOR_MIN_LENGTH
|
||||
|
||||
cdef extern int FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
cdef extern int FORMAT_MESSAGE_FROM_SYSTEM
|
||||
cdef extern int FORMAT_MESSAGE_IGNORE_INSERTS
|
||||
|
||||
|
||||
cdef extern from 'accctrl.h':
|
||||
ctypedef enum _SE_OBJECT_TYPE:
|
||||
SE_FILE_OBJECT
|
||||
ctypedef _SE_OBJECT_TYPE SE_OBJECT_TYPE
|
||||
struct _TRUSTEE_W:
|
||||
uint16_t TrusteeForm
|
||||
uint16_t TrusteeType
|
||||
LPCTSTR ptstrName
|
||||
|
||||
struct _EXPLICIT_ACCESS_W:
|
||||
DWORD grfAccessPermissions
|
||||
uint16_t grfAccessMode
|
||||
DWORD grfInheritance
|
||||
_TRUSTEE_W Trustee
|
||||
|
||||
cdef extern uint16_t TRUSTEE_IS_SID
|
||||
cdef extern uint16_t TRUSTEE_IS_NAME
|
||||
cdef extern uint16_t TRUSTEE_BAD_FORM
|
||||
|
||||
DWORD GetExplicitEntriesFromAclW(_ACL*, uint32_t*, _EXPLICIT_ACCESS_W**)
|
||||
|
||||
|
||||
cdef extern from 'Sddl.h':
|
||||
ctypedef int* LPBOOL
|
||||
|
||||
BOOL GetFileSecurityW(LPCTSTR, int, PSID, DWORD, LPDWORD)
|
||||
BOOL GetSecurityDescriptorOwner(PSID, PSID*, LPBOOL)
|
||||
BOOL LookupAccountSidW(LPCTSTR, PSID, LPCTSTR, LPDWORD, LPCTSTR, LPDWORD, uint16_t*)
|
||||
BOOL ConvertSidToStringSidW(PSID, LPCTSTR*)
|
||||
BOOL ConvertStringSidToSidW(LPCTSTR, PSID*)
|
||||
BOOL ConvertSecurityDescriptorToStringSecurityDescriptorW(BYTE*, DWORD, int, LPCTSTR*, int*)
|
||||
|
||||
cdef extern int SDDL_REVISION_1
|
||||
|
||||
|
||||
cdef extern from 'Aclapi.h':
|
||||
ctypedef void* PACL
|
||||
DWORD GetNamedSecurityInfoW(LPCTSTR, SE_OBJECT_TYPE, DWORD, PSID*, PSID*, PACL*, PACL*, _ACL**)
|
||||
DWORD SetNamedSecurityInfoW(LPCTSTR, int, int, PSID, PSID, PACL, PACL)
|
||||
DWORD SetEntriesInAclW(unsigned int, _EXPLICIT_ACCESS_W*, PACL, _ACL**)
|
||||
|
||||
|
||||
def raise_error(api, path=''):
|
||||
cdef wchar_t *error_message
|
||||
error = GetLastError()
|
||||
if not error:
|
||||
return
|
||||
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, error, 0, &error_message, 0, NULL)
|
||||
error_string = PyUnicode_FromWideChar(error_message, -1)
|
||||
LocalFree(<HLOCAL>error_message)
|
||||
error_string = api + ': ' + error_string
|
||||
if path:
|
||||
raise OSError(error, error_string, path)
|
||||
else:
|
||||
raise OSError(error, error_string)
|
||||
|
||||
|
||||
cdef PSID _get_file_security(filename, int request):
|
||||
cdef DWORD length = 0
|
||||
# N.B. This query may fail with ERROR_INVALID_FUNCTION
|
||||
# for some filesystems.
|
||||
cdef wchar_t* wcharfilename = PyUnicode_AsWideCharString(filename, NULL)
|
||||
GetFileSecurityW(wcharfilename, request, NULL, 0, &length)
|
||||
if GetLastError() == ERROR_INSUFFICIENT_BUFFER:
|
||||
SetLastError(0)
|
||||
else:
|
||||
raise_error('GetFileSecurityW', filename)
|
||||
return NULL
|
||||
cdef BYTE* sd = <BYTE*>malloc((length) * sizeof(BYTE))
|
||||
GetFileSecurityW(wcharfilename, request, sd, length, &length)
|
||||
PyMem_Free(wcharfilename)
|
||||
return sd
|
||||
|
||||
|
||||
cdef PSID _get_security_descriptor_owner(PSID sd):
|
||||
cdef PSID sid
|
||||
cdef BOOL sid_defaulted
|
||||
GetSecurityDescriptorOwner(sd, &sid, &sid_defaulted)
|
||||
return (sid)
|
||||
|
||||
|
||||
cdef _look_up_account_sid(PSID sid):
|
||||
cdef int SIZE = 256
|
||||
cdef wchar_t* name = <wchar_t*>malloc((SIZE) * sizeof(wchar_t))
|
||||
cdef wchar_t* domain = <wchar_t*>malloc((SIZE) * sizeof(wchar_t))
|
||||
cdef DWORD cch_name = SIZE
|
||||
cdef DWORD cch_domain = SIZE
|
||||
cdef uint16_t sid_type = <uint16_t>0
|
||||
|
||||
cdef BOOL ret = LookupAccountSidW(NULL, sid, name, &cch_name, domain, &cch_domain, &sid_type)
|
||||
if ret == 0:
|
||||
lasterror = GetLastError()
|
||||
if lasterror == ERROR_NONE_MAPPED:
|
||||
# Unknown (removed?) user or file from another windows installation
|
||||
free(name)
|
||||
free(domain)
|
||||
return 'unknown', 'unknown', 0
|
||||
else:
|
||||
raise_error('LookupAccountSidW')
|
||||
|
||||
pystrName = PyUnicode_FromWideChar(name, -1)
|
||||
pystrDomain = PyUnicode_FromWideChar(domain, -1)
|
||||
|
||||
free(name)
|
||||
free(domain)
|
||||
return pystrName, pystrDomain, <unsigned int>sid_type
|
||||
|
||||
|
||||
cdef sid2string(PSID sid):
|
||||
cdef wchar_t* sidstr
|
||||
ConvertSidToStringSidW(sid, &sidstr)
|
||||
ret = PyUnicode_FromWideChar(sidstr, -1)
|
||||
LocalFree(<HLOCAL>sidstr)
|
||||
return ret
|
||||
|
||||
|
||||
def get_owner(path):
|
||||
cdef int request = OWNER_SECURITY_INFORMATION
|
||||
cdef BYTE* sd = _get_file_security(path, request)
|
||||
if sd == NULL:
|
||||
return 'unknown', 'S-1-0-0'
|
||||
cdef PSID sid = _get_security_descriptor_owner(sd)
|
||||
if sid == NULL:
|
||||
return 'unknown', 'S-1-0-0'
|
||||
name, domain, sid_type = _look_up_account_sid(sid)
|
||||
free(sd)
|
||||
if domain and domain.lower() != platform.node().lower() and domain != 'BUILTIN':
|
||||
return '{0}\\{1}'.format(domain, name), sid2string(sid)
|
||||
else:
|
||||
return name, sid2string(sid)
|
||||
|
||||
|
||||
def set_owner(path, owner, sidstring = None):
|
||||
cdef PSID newsid
|
||||
cdef wchar_t* temp
|
||||
cdef DWORD sid_type = 0
|
||||
cdef DWORD length = 0
|
||||
cdef DWORD domainlength = 0
|
||||
if sidstring is not None:
|
||||
temp = PyUnicode_AsWideCharString(sidstring, NULL)
|
||||
ConvertStringSidToSidW(temp, &newsid)
|
||||
if sidstring is None or GetLastError() == ERROR_INVALID_SID:
|
||||
temp = PyUnicode_AsWideCharString(owner, NULL)
|
||||
|
||||
LookupAccountNameW(NULL, temp, NULL, &length, NULL, &domainlength, &sid_type)
|
||||
|
||||
newsid = <PSID>malloc((length) * sizeof(BYTE))
|
||||
SetLastError(0)
|
||||
domainlength = 0
|
||||
LookupAccountNameW(NULL, temp, newsid, &length, NULL, &domainlength, &sid_type)
|
||||
if GetLastError() != 0:
|
||||
raise_error('LookupAccountNameW', owner)
|
||||
PyMem_Free(temp)
|
||||
return
|
||||
|
||||
PyMem_Free(temp)
|
||||
|
||||
cdef wchar_t* cstrPath = PyUnicode_AsWideCharString(path, NULL)
|
||||
SetNamedSecurityInfoW(cstrPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, newsid, NULL, NULL, NULL)
|
||||
PyMem_Free(cstrPath)
|
||||
if length == 0:
|
||||
LocalFree(<HLOCAL>newsid)
|
||||
else:
|
||||
free(newsid)
|
||||
|
||||
|
||||
def acl_get(path, item, st, numeric_owner=False):
|
||||
cdef int request = DACL_SECURITY_INFORMATION
|
||||
|
||||
cdef BYTE* SD = _get_file_security(path, request)
|
||||
if SD == NULL:
|
||||
return
|
||||
|
||||
cdef BOOL daclFound
|
||||
cdef _ACL* DACL
|
||||
cdef BOOL DACLDefaulted
|
||||
GetSecurityDescriptorDacl(SD, &daclFound, &DACL, &DACLDefaulted)
|
||||
|
||||
cdef uint32_t length
|
||||
cdef _EXPLICIT_ACCESS_W* ACEs
|
||||
|
||||
GetExplicitEntriesFromAclW(DACL, &length, &ACEs)
|
||||
|
||||
pyDACL = []
|
||||
cdef PSID newsid
|
||||
cdef uint32_t domainlength
|
||||
cdef uint32_t sid_type
|
||||
for i in range(length):
|
||||
permissions = None
|
||||
name = ""
|
||||
sidstr = ""
|
||||
if ACEs[i].Trustee.TrusteeForm == TRUSTEE_IS_SID:
|
||||
name, domain, type = _look_up_account_sid(<BYTE*>(ACEs[i].Trustee.ptstrName))
|
||||
sidstr = sid2string(<PSID>(ACEs[i].Trustee.ptstrName))
|
||||
|
||||
elif ACEs[i].Trustee.TrusteeForm == TRUSTEE_IS_NAME:
|
||||
sid_type = 0
|
||||
domainlength = 0
|
||||
LookupAccountNameW(NULL, ACEs[i].Trustee.ptstrName, NULL, &(length), NULL, &domainlength, &sid_type)
|
||||
|
||||
newsid = <PSID>malloc((length) * sizeof(BYTE))
|
||||
domainlength = 0
|
||||
LookupAccountNameW(NULL, ACEs[i].Trustee.ptstrName, newsid, &length, NULL, &domainlength, &sid_type)
|
||||
trusteeName, domain, type = _look_up_account_sid(newsid)
|
||||
|
||||
name = trusteeName
|
||||
sidstr = sid2string(newsid)
|
||||
free(newsid)
|
||||
|
||||
elif ACEs[i].Trustee.TrusteeForm == TRUSTEE_BAD_FORM:
|
||||
continue
|
||||
permissions = {'user': {'name': name, 'sid': sidstr}, 'permissions': (ACEs[i].grfAccessPermissions, ACEs[i].grfAccessMode, ACEs[i].grfInheritance)}
|
||||
pyDACL.append(permissions)
|
||||
item[b'win_dacl'] = safe_encode(json.dumps(pyDACL))
|
||||
|
||||
free(SD)
|
||||
LocalFree(<HLOCAL>ACEs)
|
||||
|
||||
|
||||
def acl_set(path, item, numeric_owner=False):
|
||||
if b'win_dacl' not in item:
|
||||
return
|
||||
|
||||
pyDACL = json.loads(safe_decode(item[b'win_dacl']))
|
||||
cdef _EXPLICIT_ACCESS_W* ACEs = <_EXPLICIT_ACCESS_W*>calloc(sizeof(_EXPLICIT_ACCESS_W), len(pyDACL))
|
||||
cdef wchar_t* temp
|
||||
cdef PSID newsid
|
||||
for i in range(len(pyDACL)):
|
||||
if pyDACL[i]['user']['name'] == '' or numeric_owner:
|
||||
ACEs[i].Trustee.TrusteeForm = TRUSTEE_IS_SID
|
||||
temp = PyUnicode_AsWideCharString(pyDACL[i]['user']['sid'], NULL)
|
||||
ConvertStringSidToSidW(temp, &newsid)
|
||||
ACEs[i].Trustee.ptstrName = <LPCTSTR>newsid
|
||||
PyMem_Free(temp)
|
||||
else:
|
||||
ACEs[i].Trustee.TrusteeForm = TRUSTEE_IS_NAME
|
||||
ACEs[i].Trustee.ptstrName = PyUnicode_AsWideCharString(pyDACL[i]['user']['name'], NULL)
|
||||
ACEs[i].grfAccessPermissions = pyDACL[i]['permissions'][0]
|
||||
ACEs[i].grfAccessMode = pyDACL[i]['permissions'][1]
|
||||
ACEs[i].grfInheritance = pyDACL[i]['permissions'][2]
|
||||
cdef _ACL* newDACL
|
||||
SetEntriesInAclW(len(pyDACL), ACEs, NULL, &newDACL)
|
||||
cdef wchar_t* cstrPath = PyUnicode_AsWideCharString(path, NULL)
|
||||
SetNamedSecurityInfoW(cstrPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, newDACL, NULL)
|
||||
|
||||
for i in range(len(pyDACL)):
|
||||
if pyDACL[i]['user']['name'] == '' or numeric_owner:
|
||||
LocalFree(<HLOCAL>ACEs[i].Trustee.ptstrName)
|
||||
else:
|
||||
PyMem_Free(ACEs[i].Trustee.ptstrName)
|
||||
free(ACEs)
|
||||
PyMem_Free(cstrPath)
|
||||
LocalFree(<HLOCAL>newDACL)
|
||||
10
setup.py
10
setup.py
|
|
@ -44,6 +44,7 @@ hashindex_source = 'borg/hashindex.pyx'
|
|||
platform_linux_source = 'borg/platform_linux.pyx'
|
||||
platform_darwin_source = 'borg/platform_darwin.pyx'
|
||||
platform_freebsd_source = 'borg/platform_freebsd.pyx'
|
||||
platform_windows_source = 'borg/platform_windows.pyx'
|
||||
|
||||
try:
|
||||
from Cython.Distutils import build_ext
|
||||
|
|
@ -52,7 +53,8 @@ try:
|
|||
class Sdist(sdist):
|
||||
def __init__(self, *args, **kwargs):
|
||||
for src in glob('borg/*.pyx'):
|
||||
cython_compiler.compile(src, cython_compiler.default_options)
|
||||
options = cython_compiler.default_options
|
||||
cython_compiler.compile(src, options)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def make_distribution(self):
|
||||
|
|
@ -64,6 +66,7 @@ try:
|
|||
'borg/platform_linux.c',
|
||||
'borg/platform_freebsd.c',
|
||||
'borg/platform_darwin.c',
|
||||
'borg/platform_windows.c',
|
||||
])
|
||||
super().make_distribution()
|
||||
|
||||
|
|
@ -79,10 +82,11 @@ except ImportError:
|
|||
platform_linux_source = platform_linux_source.replace('.pyx', '.c')
|
||||
platform_freebsd_source = platform_freebsd_source.replace('.pyx', '.c')
|
||||
platform_darwin_source = platform_darwin_source.replace('.pyx', '.c')
|
||||
platform_windows_source = platform_windows_source.replace('.pyx', '.c')
|
||||
from distutils.command.build_ext import build_ext
|
||||
if not on_rtd and not all(os.path.exists(path) for path in [
|
||||
compress_source, crypto_source, chunker_source, hashindex_source,
|
||||
platform_linux_source, platform_freebsd_source]):
|
||||
platform_linux_source, platform_freebsd_source, platform_windows_source]):
|
||||
raise ImportError('The GIT version of Borg needs Cython. Install Cython or use a released version.')
|
||||
|
||||
|
||||
|
|
@ -312,6 +316,8 @@ if not on_rtd:
|
|||
ext_modules.append(Extension('borg.platform_freebsd', [platform_freebsd_source]))
|
||||
elif sys.platform == 'darwin':
|
||||
ext_modules.append(Extension('borg.platform_darwin', [platform_darwin_source]))
|
||||
elif sys.platform == 'win32':
|
||||
ext_modules.append(Extension('borg.platform_windows', [platform_windows_source]))
|
||||
|
||||
|
||||
def parse(root, describe_command=None):
|
||||
|
|
|
|||
Loading…
Reference in a new issue