@@ -, +, @@ --- cnf/make.globals | 14 +++++++ pym/portage/const.py | 29 ++------------- pym/portage/package/ebuild/doebuild.py | 68 +++++++++++++++++++++++++++------- 3 files changed, 73 insertions(+), 38 deletions(-) --- a/cnf/make.globals +++ a/cnf/make.globals @@ -149,6 +149,20 @@ PORTAGE_ELOG_MAILFROM="@portageuser@@localhost" # Signing command used by repoman PORTAGE_GPG_SIGNING_COMMAND="gpg --sign --digest-algo SHA256 --clearsign --yes --default-key \"\${PORTAGE_GPG_KEY}\" --homedir \"\${PORTAGE_GPG_DIR}\" \"\${FILE}\"" +# Writeable paths for Mac OS X seatbelt sandbox +# +# If path ends in a slash (/), access will recursively be allowed to directory +# contents (using a regex), not the directory itself. Without a slash, access +# to the directory or file itself will be allowed (using a literal), so it can +# be created, removed and changed. If both is needed, the directory needs to be +# given twice, once with and once without the slash. Obviously this only makes +# sense for directories, not files. +# +# An empty value for either variable will disable all restrictions on the +# corresponding operation. +MACOSSANDBOX_PATHS="/dev/fd/ /private/tmp/ /private/var/tmp/ @@PORTAGE_BUILDDIR@@/ @@PORTAGE_ACTUAL_DISTDIR@@/" +MACOSSANDBOX_PATHS_CONTENT_ONLY="/dev/null /dev/dtracehelper /dev/tty /private/var/run/syslog" + # ***************************** # ** DO NOT EDIT THIS FILE ** # *************************************************** --- a/pym/portage/const.py +++ a/pym/portage/const.py @@ -78,33 +78,12 @@ MOVE_BINARY = PORTAGE_MV PRELINK_BINARY = "/usr/sbin/prelink" MACOSSANDBOX_BINARY = "/usr/bin/sandbox-exec" MACOSSANDBOX_PROFILE = '''(version 1) - (allow default) - (deny file-write*) - -(allow file-read* file-write* - (literal - ;;#"@@PORTAGE_BUILDDIR@@" - ;;#"@@PORTAGE_ACTUAL_DISTDIR@@" - #"/dev/tty" - #"/dev/dtracehelper" - ) - - (regex - ;;#"^@@PORTAGE_BUILDDIR_RE@@/" - ;;#"^@@PORTAGE_ACTUAL_DISTDIR_RE@@/" - #"^(/private)?/var/tmp" - #"^(/private)?/tmp" - ) -) - -(allow file-read-data file-write-data - (regex - #"^/dev/null$" - #"^(/private)?/var/run/syslog$" - ) -)''' +(allow file-write* +@@MACOSSANDBOX_PATHS@@) +(allow file-write-data +@@MACOSSANDBOX_PATHS_CONTENT_ONLY@@)''' PORTAGE_GROUPNAME = portagegroup PORTAGE_USERNAME = portageuser --- a/pym/portage/package/ebuild/doebuild.py +++ a/pym/portage/package/ebuild/doebuild.py @@ -1477,25 +1477,67 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero spawn_func = portage.process.spawn_fakeroot elif "sandbox" in features and platform.system() == 'Darwin': keywords["opt_name"] += " macossandbox" - sbprofile = MACOSSANDBOX_PROFILE - for pathvar in [ "PORTAGE_BUILDDIR", "PORTAGE_ACTUAL_DISTDIR" ]: - if pathvar not in mysettings: + + # determine variable names from profile: split + # "text@@VARNAME@@moretext@@OTHERVAR@@restoftext" into + # ("text", # "VARNAME", "moretext", "OTHERVAR", "restoftext") + # and extract variable named by reading every second item. + variables = [] + for line in sbprofile.split("\n"): + variables.extend(line.split("@@")[1:-1:2]) + + for var in variables: + paths = "" + if var in mysettings: + paths = mysettings[var] + else: + writemsg("Warning: sandbox profile references variable %s " + "which is not set.\nThe rule using it will have no " + "effect, which is most likely not the intended " + "result.\nPlease check make.conf/make.globals.\n" % + var) + + # not set or empty value + if not paths: + sbprofile = sbprofile.replace("@@%s@@" % var, "") continue - sbprefixpath = mysettings[pathvar] + rules_literal = "" + rules_regex = "" - # escape some characters with special meaning in re's - sbprefixre = sbprefixpath.replace("+", "\+") - sbprefixre = sbprefixre.replace("*", "\*") - sbprefixre = sbprefixre.replace("[", "\[") - sbprefixre = sbprefixre.replace("[", "\[") + # FIXME: Allow for quoting inside the variable to allow paths with + # spaces in them? + for path in paths.split(" "): + # do a second round of token replacements to be able to + # reference settings like EPREFIX or PORTAGE_BUILDDIR. + for token in path.split("@@")[1:-1:2]: + if token not in mysettings: + continue - sbprofile = sbprofile.replace("@@%s@@" % pathvar, sbprefixpath) - sbprofile = sbprofile.replace("@@%s_RE@@" % pathvar, sbprefixre) + path = path.replace("@@%s@@" % token, mysettings[token]) - # uncomment all rules that don't contain any @@'s any more - sbprofile = re.sub(r';;(#"[^@"]*")', r'\1', sbprofile) + if "@@" in path: + # unreplaced tokens left - silently ignore path - needed + # for PORTAGE_ACTUAL_DISTDIR which isn't always set + pass + elif path[-1] == os.sep: + # path ends in slash - make it a regex and allow access + # recursively. + path = path.replace("+", "\+") + path = path.replace("*", "\*") + path = path.replace("[", "\[") + path = path.replace("[", "\[") + rules_regex += " #\"^%s\"\n" % path + else: + rules_literal += " #\"%s\"\n" % path + + rules = "" + if rules_literal: + rules += " (literal\n" + rules_literal + " )\n" + if rules_regex: + rules += " (regex\n" + rules_regex + " )\n" + sbprofile = sbprofile.replace("@@%s@@" % var, rules) keywords["profile"] = sbprofile spawn_func = portage.process.spawn_macossandbox --