Add new Sierra-compatible fileshare mounting code to manifestutil.

This commit is contained in:
Greg Neagle
2016-10-21 15:38:58 -07:00
parent 260418b7dd
commit e9bdb65ade

View File

@@ -32,6 +32,8 @@ import sys
import thread
import time
import objc
from ctypes.util import find_library
from xml.parsers.expat import ExpatError
@@ -43,6 +45,105 @@ except ImportError:
'''Placeholder if munkilib is not available'''
return 'UNKNOWN'
# NetFS share mounting code borrowed and liberally adapted from Michael Lynn's
# work here: https://gist.github.com/pudquick/1362a8908be01e23041d
try:
import errno
import getpass
import objc
from CoreFoundation import CFURLCreateWithString
class Attrdict(dict):
'''Custom dict class'''
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
NetFS = Attrdict()
# Can cheat and provide 'None' for the identifier, it'll just use
# frameworkPath instead
# scan_classes=False means only add the contents of this Framework
NetFS_bundle = objc.initFrameworkWrapper(
'NetFS', frameworkIdentifier=None,
frameworkPath=objc.pathForFramework('NetFS.framework'),
globals=NetFS, scan_classes=False)
# https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/
# ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
# Fix NetFSMountURLSync signature
del NetFS['NetFSMountURLSync']
objc.loadBundleFunctions(
NetFS_bundle, NetFS, [('NetFSMountURLSync', 'i@@@@@@o^@')])
NETFSMOUNTURLSYNC_AVAILABLE = True
except (ImportError, KeyError):
NETFSMOUNTURLSYNC_AVAILABLE = False
if NETFSMOUNTURLSYNC_AVAILABLE:
class ShareMountException(Exception):
'''An exception raised if share mounting failed'''
pass
class ShareAuthenticationNeededException(ShareMountException):
'''An exception raised if authentication is needed'''
pass
def mount_share(share_url):
'''Mounts a share at /Volumes, returns the mount point or raises an
error'''
sh_url = CFURLCreateWithString(None, share_url, None)
# Set UI to reduced interaction
open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI}
# Allow mounting sub-directories of root shares
mount_options = {NetFS.kNetFSAllowSubMountsKey: True}
# Mount!
result, output = NetFS.NetFSMountURLSync(
sh_url, None, None, None, open_options, mount_options, None)
# Check if it worked
if result != 0:
if result in (errno.ENOTSUP, errno.EAUTH):
# errno.ENOTSUP is returned if an afp share needs a login
# errno.EAUTH is returned if authentication fails (SMB for sure)
raise ShareAuthenticationNeededException()
raise ShareMountException(
'Error mounting url "%s": %s, error %s'
% (share_url, os.strerror(result), result))
# Return the mountpath
return str(output[0])
def mount_share_with_credentials(share_url, username, password):
'''Mounts a share at /Volumes, returns the mount point or raises an
error. Include username and password as parameters, not in the
share_path URL'''
sh_url = CFURLCreateWithString(None, share_url, None)
# Set UI to reduced interaction
open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI}
# Allow mounting sub-directories of root shares
mount_options = {NetFS.kNetFSAllowSubMountsKey: True}
# Mount!
result, output = NetFS.NetFSMountURLSync(
sh_url, None, username, password, open_options, mount_options, None)
# Check if it worked
if result != 0:
raise ShareMountException(
'Error mounting url "%s": %s, error %s'
% (share_url, os.strerror(result), result))
# Return the mountpath
return str(output[0])
def mount_share_url(share_url):
'''Mount a share url under /Volumes, prompting for password if needed
Raises ShareMountException if there's an error'''
try:
mount_share(share_url)
except ShareAuthenticationNeededException:
username = raw_input('Username: ')
password = getpass.getpass()
mount_share_with_credentials(share_url, username, password)
try:
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
# No name 'Foo' in module 'Bar' warnings. Disable them.
@@ -337,22 +438,32 @@ def mount_repo_cli():
repo_url = pref('repo_url')
if os.path.exists(repo_path):
return
os.mkdir(repo_path)
print 'Attempting to mount fileshare %s:' % repo_url
if repo_url.startswith('afp:'):
cmd = ['/sbin/mount_afp', '-i', repo_url, repo_path]
elif repo_url.startswith('smb:'):
cmd = ['/sbin/mount_smbfs', repo_url[4:], repo_path]
elif repo_url.startswith('nfs://'):
cmd = ['/sbin/mount_nfs', repo_url[6:], repo_path]
print 'Attempting to mount fileshare %s:' % repo_path
if NETFSMOUNTURLSYNC_AVAILABLE:
# mount the share using the NetFS API
try:
mount_share_url(repo_url)
except ShareMountException, err:
print >> sys.stderr, err
else:
WE_MOUNTED_THE_REPO = True
else:
print >> sys.stderr, 'Unsupported filesystem URL!'
return
retcode = subprocess.call(cmd)
if retcode:
os.rmdir(repo_path)
else:
WE_MOUNTED_THE_REPO = True
# do it the old way
os.mkdir(REPO_PATH)
if REPO_URL.startswith('afp:'):
cmd = ['/sbin/mount_afp', '-i', repo_url, repo_path]
elif REPO_URL.startswith('smb:'):
cmd = ['/sbin/mount_smbfs', repo_url[4:], repo_path]
elif REPO_URL.startswith('nfs://'):
cmd = ['/sbin/mount_nfs', repo_url[6:], repo_path]
else:
print >> sys.stderr, 'Unsupported filesystem URL!'
return
retcode = subprocess.call(cmd)
if retcode:
os.rmdir(repo_path)
else:
WE_MOUNTED_THE_REPO = True
def unmount_repo_cli():