allow.py revision 209962
128219Smsmith#! /usr/bin/python2.4 242475Snsouch# 328219Smsmith# CDDL HEADER START 428219Smsmith# 528219Smsmith# The contents of this file are subject to the terms of the 628219Smsmith# Common Development and Distribution License (the "License"). 728219Smsmith# You may not use this file except in compliance with the License. 828219Smsmith# 928219Smsmith# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 1028219Smsmith# or http://www.opensolaris.org/os/licensing. 1128219Smsmith# See the License for the specific language governing permissions 1228219Smsmith# and limitations under the License. 1328219Smsmith# 1428219Smsmith# When distributing Covered Code, include this CDDL HEADER in each 1528219Smsmith# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1628219Smsmith# If applicable, add the following below this CDDL HEADER, with the 1728219Smsmith# fields enclosed by brackets "[]" replaced with your own identifying 1828219Smsmith# information: Portions Copyright [yyyy] [name of copyright owner] 1928219Smsmith# 2028219Smsmith# CDDL HEADER END 2128219Smsmith# 2228219Smsmith# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2328219Smsmith# Use is subject to license terms. 2428219Smsmith# 2528219Smsmith 2628219Smsmith"""This module implements the "zfs allow" and "zfs unallow" subcommands. 2728219SmsmithThe only public interface is the zfs.allow.do_allow() function.""" 28119418Sobrien 29119418Sobrienimport zfs.util 30119418Sobrienimport zfs.dataset 3155939Snsouchimport optparse 3255939Snsouchimport sys 3328219Smsmithimport pwd 3428219Smsmithimport grp 3555939Snsouchimport errno 36187576Sjhb 3755939Snsouch_ = zfs.util._ 38187576Sjhb 3955939Snsouchclass FSPerms(object): 4028219Smsmith """This class represents all the permissions that are set on a 41175222Sjhb particular filesystem (not including those inherited).""" 4228219Smsmith 43175222Sjhb __slots__ = "create", "sets", "local", "descend", "ld" 44175222Sjhb __repr__ = zfs.util.default_repr 4528219Smsmith 4628257Smsmith def __init__(self, raw): 4728219Smsmith """Create a FSPerms based on the dict of raw permissions 4855939Snsouch from zfs.ioctl.get_fsacl().""" 49185003Sjhb # set of perms 5055939Snsouch self.create = set() 51185003Sjhb 5269774Sphk # below are { "Ntype name": set(perms) } 5342475Snsouch # where N is a number that we just use for sorting, 5428219Smsmith # type is "user", "group", "everyone", or "" (for sets) 55187576Sjhb # name is a user, group, or set name, or "" (for everyone) 56187576Sjhb self.sets = dict() 5728219Smsmith self.local = dict() 5855939Snsouch self.descend = dict() 5928219Smsmith self.ld = dict() 6028219Smsmith 61184130Sjhb # see the comment in dsl_deleg.c for the definition of whokey 6255939Snsouch for whokey in raw.keys(): 6355939Snsouch perms = raw[whokey].keys() 6455939Snsouch whotypechr = whokey[0].lower() 65184130Sjhb ws = whokey[3:] 6628219Smsmith if whotypechr == "c": 67184130Sjhb self.create.update(perms) 6855939Snsouch elif whotypechr == "s": 6955939Snsouch nwho = "1" + ws 7055939Snsouch self.sets.setdefault(nwho, set()).update(perms) 7155939Snsouch else: 72184130Sjhb if whotypechr == "u": 7355939Snsouch try: 74184130Sjhb name = pwd.getpwuid(int(ws)).pw_name 7555939Snsouch except KeyError: 76184130Sjhb name = ws 7755939Snsouch nwho = "1user " + name 7855939Snsouch elif whotypechr == "g": 7955939Snsouch try: 8055939Snsouch name = grp.getgrgid(int(ws)).gr_name 8155939Snsouch except KeyError: 8255939Snsouch name = ws 8355939Snsouch nwho = "2group " + name 8455939Snsouch elif whotypechr == "e": 8555939Snsouch nwho = "3everyone" 8655939Snsouch else: 8728219Smsmith raise ValueError(whotypechr) 8856455Speter 8928219Smsmith if whokey[1] == "l": 9055939Snsouch d = self.local 9128219Smsmith elif whokey[1] == "d": 9256455Speter d = self.descend 93212413Savg else: 9428219Smsmith raise ValueError(whokey[1]) 9555939Snsouch 9655939Snsouch d.setdefault(nwho, set()).update(perms) 97185003Sjhb 9855939Snsouch # Find perms that are in both local and descend, and 9969781Sdwmalone # move them to ld. 10069781Sdwmalone for nwho in self.local: 10155939Snsouch if nwho not in self.descend: 102185003Sjhb continue 10328219Smsmith # note: these are set operations 10455939Snsouch self.ld[nwho] = self.local[nwho] & self.descend[nwho] 10555939Snsouch self.local[nwho] -= self.ld[nwho] 10628219Smsmith self.descend[nwho] -= self.ld[nwho] 10755939Snsouch 10855939Snsouch @staticmethod 10956455Speter def __ldstr(d, header): 11055939Snsouch s = "" 11128219Smsmith for (nwho, perms) in sorted(d.items()): 112185003Sjhb # local and descend may have entries where perms 11355939Snsouch # is an empty set, due to consolidating all 11455939Snsouch # permissions into ld 11555939Snsouch if perms: 11655939Snsouch s += "\t%s %s\n" % \ 11756455Speter (nwho[1:], ",".join(sorted(perms))) 118185003Sjhb if s: 11955939Snsouch s = header + s 12055939Snsouch return s 12155939Snsouch 12255939Snsouch def __str__(self): 12355939Snsouch s = self.__ldstr(self.sets, _("Permission sets:\n")) 12455939Snsouch 12555939Snsouch if self.create: 12628219Smsmith s += _("Create time permissions:\n") 127185003Sjhb s += "\t%s\n" % ",".join(sorted(self.create)) 12855939Snsouch 12928219Smsmith s += self.__ldstr(self.local, _("Local permissions:\n")) 130185003Sjhb s += self.__ldstr(self.descend, _("Descendent permissions:\n")) 13155939Snsouch s += self.__ldstr(self.ld, _("Local+Descendent permissions:\n")) 132185003Sjhb return s.rstrip() 13355939Snsouch 13428219Smsmithdef args_to_perms(parser, options, who, perms): 13555939Snsouch """Return a dict of raw perms {"whostr" -> {"perm" -> None}} 13655939Snsouch based on the command-line input.""" 13755939Snsouch 138184130Sjhb # perms is not set if we are doing a "zfs unallow <who> <fs>" to 13955939Snsouch # remove all of someone's permissions 14055939Snsouch if perms: 14155939Snsouch setperms = dict(((p, None) for p in perms if p[0] == "@")) 14255939Snsouch baseperms = dict(((canonicalized_perm(p), None) 14355939Snsouch for p in perms if p[0] != "@")) 14455939Snsouch else: 145182014Sjhb setperms = None 14655939Snsouch baseperms = None 14742475Snsouch 14842475Snsouch d = dict() 14942475Snsouch 15042475Snsouch def storeperm(typechr, inheritchr, arg): 15142475Snsouch assert typechr in "ugecs" 15242475Snsouch assert inheritchr in "ld-" 15342475Snsouch 15442475Snsouch def mkwhokey(t): 15542475Snsouch return "%c%c$%s" % (t, inheritchr, arg) 15642475Snsouch 15742475Snsouch if baseperms or not perms: 15842475Snsouch d[mkwhokey(typechr)] = baseperms 15942475Snsouch if setperms or not perms: 16028257Smsmith d[mkwhokey(typechr.upper())] = setperms 16128257Smsmith 16228257Smsmith def decodeid(w, toidfunc, fmt): 16328257Smsmith try: 16442475Snsouch return int(w) 16528257Smsmith except ValueError: 16628257Smsmith try: 16728257Smsmith return toidfunc(w)[2] 16828257Smsmith except KeyError: 16928257Smsmith parser.error(fmt % w) 17042475Snsouch 17128257Smsmith if options.set: 17228219Smsmith storeperm("s", "-", who) 17328257Smsmith elif options.create: 17428257Smsmith storeperm("c", "-", "") 175249582Sgabor else: 17628257Smsmith for w in who: 17728257Smsmith if options.user: 17828257Smsmith id = decodeid(w, pwd.getpwnam, 17928257Smsmith _("invalid user %s")) 18078132Speter typechr = "u" 18128257Smsmith elif options.group: 18228257Smsmith id = decodeid(w, grp.getgrnam, 18328257Smsmith _("invalid group %s")) 18428257Smsmith typechr = "g" 18528257Smsmith elif w == "everyone": 18675061Salfred id = "" 18728257Smsmith typechr = "e" 18828257Smsmith else: 18975061Salfred try: 19028257Smsmith id = pwd.getpwnam(w)[2] 19128257Smsmith typechr = "u" 19228257Smsmith except KeyError: 19328257Smsmith try: 19475061Salfred id = grp.getgrnam(w)[2] 19528257Smsmith typechr = "g" 19628257Smsmith except KeyError: 19728257Smsmith parser.error(_("invalid user/group %s") % w) 19828257Smsmith if options.local: 19928257Smsmith storeperm(typechr, "l", id) 20028257Smsmith if options.descend: 20128257Smsmith storeperm(typechr, "d", id) 20228257Smsmith return d 20328257Smsmith 20428257Smsmithperms_subcmd = dict( 20528257Smsmith create=_("Must also have the 'mount' ability"), 20628257Smsmith destroy=_("Must also have the 'mount' ability"), 20755939Snsouch snapshot=_("Must also have the 'mount' ability"), 20828257Smsmith rollback=_("Must also have the 'mount' ability"), 20942475Snsouch clone=_("""Must also have the 'create' ability and 'mount' 21028257Smsmith\t\t\t\tability in the origin file system"""), 21138061Smsmith promote=_("""Must also have the 'mount' 21228257Smsmith\t\t\t\tand 'promote' ability in the origin file system"""), 21328257Smsmith rename=_("""Must also have the 'mount' and 'create' 214184130Sjhb\t\t\t\tability in the new parent"""), 215185003Sjhb receive=_("Must also have the 'mount' and 'create' ability"), 21655939Snsouch allow=_("Must also have the permission that is being\n\t\t\t\tallowed"), 21742475Snsouch mount=_("Allows mount/umount of ZFS datasets"), 21838061Smsmith share=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"), 21939134Snsouch send="", 22042475Snsouch) 221184130Sjhb 22242475Snsouchperms_other = dict( 22342475Snsouch userprop=_("Allows changing any user property"), 22442475Snsouch userquota=_("Allows accessing any userquota@... property"), 22542475Snsouch groupquota=_("Allows accessing any groupquota@... property"), 22628257Smsmith userused=_("Allows reading any userused@... property"), 22728257Smsmith groupused=_("Allows reading any groupused@... property"), 22828257Smsmith) 22928257Smsmith 23028257Smsmithdef hasset(ds, setname): 23142475Snsouch """Return True if the given setname (string) is defined for this 23242475Snsouch ds (Dataset).""" 233184130Sjhb # It would be nice to cache the result of get_fsacl(). 23428257Smsmith for raw in ds.get_fsacl().values(): 23528257Smsmith for whokey in raw.keys(): 236184130Sjhb if whokey[0].lower() == "s" and whokey[3:] == setname: 23728257Smsmith return True 23842475Snsouch return False 23942475Snsouch 24028257Smsmithdef canonicalized_perm(permname): 24128257Smsmith """Return the canonical name (string) for this permission (string). 24228257Smsmith Raises ZFSError if it is not a valid permission.""" 24328257Smsmith if permname in perms_subcmd.keys() or permname in perms_other.keys(): 24428257Smsmith return permname 24528257Smsmith try: 24628257Smsmith return zfs.dataset.getpropobj(permname).name 24728257Smsmith except KeyError: 24828257Smsmith raise zfs.util.ZFSError(errno.EINVAL, permname, 24928257Smsmith _("invalid permission")) 25028257Smsmith 25128257Smsmithdef print_perms(): 25228257Smsmith """Print the set of supported permissions.""" 25328257Smsmith print(_("\nThe following permissions are supported:\n")) 25428257Smsmith fmt = "%-16s %-14s\t%s" 25528257Smsmith print(fmt % (_("NAME"), _("TYPE"), _("NOTES"))) 25628257Smsmith 25728257Smsmith for (name, note) in sorted(perms_subcmd.iteritems()): 25828257Smsmith print(fmt % (name, _("subcommand"), note)) 25928257Smsmith 26042475Snsouch for (name, note) in sorted(perms_other.iteritems()): 26142475Snsouch print(fmt % (name, _("other"), note)) 26228257Smsmith 26328257Smsmith for (name, prop) in sorted(zfs.dataset.proptable.iteritems()): 26428257Smsmith if prop.visible and prop.delegatable(): 26528257Smsmith print(fmt % (name, _("property"), "")) 26628257Smsmith 26728257Smsmithdef do_allow(): 26828257Smsmith """Implementes the "zfs allow" and "zfs unallow" subcommands.""" 26928257Smsmith un = (sys.argv[1] == "unallow") 27028257Smsmith 27138061Smsmith def usage(msg=None): 27238061Smsmith parser.print_help() 27328257Smsmith print_perms() 27428257Smsmith if msg: 27528257Smsmith print 27638061Smsmith parser.exit("zfs: error: " + msg) 27738061Smsmith else: 27838061Smsmith parser.exit() 27942475Snsouch 28042475Snsouch if un: 28139134Snsouch u = _("""unallow [-rldug] <"everyone"|user|group>[,...] 28242475Snsouch [<perm|@setname>[,...]] <filesystem|volume> 28342475Snsouch unallow [-rld] -e [<perm|@setname>[,...]] <filesystem|volume> 28442475Snsouch unallow [-r] -c [<perm|@setname>[,...]] <filesystem|volume> 28542475Snsouch unallow [-r] -s @setname [<perm|@setname>[,...]] <filesystem|volume>""") 28642475Snsouch verb = _("remove") 28742475Snsouch sstr = _("undefine permission set") 28855939Snsouch else: 28942475Snsouch u = _("""allow <filesystem|volume> 29055939Snsouch allow [-ldug] <"everyone"|user|group>[,...] <perm|@setname>[,...] 29142475Snsouch <filesystem|volume> 29242475Snsouch allow [-ld] -e <perm|@setname>[,...] <filesystem|volume> 29342475Snsouch allow -c <perm|@setname>[,...] <filesystem|volume> 294185003Sjhb allow -s @setname <perm|@setname>[,...] <filesystem|volume>""") 29542475Snsouch verb = _("set") 29642475Snsouch sstr = _("define permission set") 29742475Snsouch 29842475Snsouch parser = optparse.OptionParser(usage=u, prog="zfs") 29955939Snsouch 30042475Snsouch parser.add_option("-l", action="store_true", dest="local", 30142475Snsouch help=_("%s permission locally") % verb) 30242475Snsouch parser.add_option("-d", action="store_true", dest="descend", 30342475Snsouch help=_("%s permission for descendents") % verb) 30455939Snsouch parser.add_option("-u", action="store_true", dest="user", 30542475Snsouch help=_("%s permission for user") % verb) 306184130Sjhb parser.add_option("-g", action="store_true", dest="group", 30742475Snsouch help=_("%s permission for group") % verb) 30855939Snsouch parser.add_option("-e", action="store_true", dest="everyone", 30942475Snsouch help=_("%s permission for everyone") % verb) 31055939Snsouch parser.add_option("-c", action="store_true", dest="create", 31142475Snsouch help=_("%s create time permissions") % verb) 31242475Snsouch parser.add_option("-s", action="store_true", dest="set", help=sstr) 31355939Snsouch if un: 31442475Snsouch parser.add_option("-r", action="store_true", dest="recursive", 31555939Snsouch help=_("remove permissions recursively")) 31642475Snsouch 31742475Snsouch if len(sys.argv) == 3 and not un: 31855939Snsouch # just print the permissions on this fs 31942475Snsouch 32055939Snsouch if sys.argv[2] == "-h": 32142475Snsouch # hack to make "zfs allow -h" work 32242475Snsouch usage() 32355939Snsouch ds = zfs.dataset.Dataset(sys.argv[2]) 32442475Snsouch 32555939Snsouch p = dict() 32642475Snsouch for (fs, raw) in ds.get_fsacl().items(): 32742475Snsouch p[fs] = FSPerms(raw) 32855939Snsouch 32942475Snsouch for fs in sorted(p.keys(), reverse=True): 33055939Snsouch s = _("---- Permissions on %s ") % fs 33142475Snsouch print(s + "-" * (70-len(s))) 33242475Snsouch print(p[fs]) 33342536Snsouch return 33442536Snsouch 33555939Snsouch 33642536Snsouch (options, args) = parser.parse_args(sys.argv[2:]) 33742536Snsouch 33855939Snsouch if sum((bool(options.everyone), bool(options.user), 33942536Snsouch bool(options.group))) > 1: 34042475Snsouch parser.error(_("-u, -g, and -e are mutually exclusive")) 34155939Snsouch 34242536Snsouch def mungeargs(expected_len): 34342536Snsouch if un and len(args) == expected_len-1: 34455939Snsouch return (None, args[expected_len-2]) 34542536Snsouch elif len(args) == expected_len: 34642475Snsouch return (args[expected_len-2].split(","), 34755939Snsouch args[expected_len-1]) 34842536Snsouch else: 34942536Snsouch usage(_("wrong number of parameters")) 35055939Snsouch 35142536Snsouch if options.set: 35242475Snsouch if options.local or options.descend or options.user or \ 35355939Snsouch options.group or options.everyone or options.create: 35442536Snsouch parser.error(_("invalid option combined with -s")) 35542536Snsouch if args[0][0] != "@": 35655939Snsouch parser.error(_("invalid set name: missing '@' prefix")) 35742536Snsouch 35842475Snsouch (perms, fsname) = mungeargs(3) 35955939Snsouch who = args[0] 36042475Snsouch elif options.create: 36142536Snsouch if options.local or options.descend or options.user or \ 36255939Snsouch options.group or options.everyone or options.set: 36342536Snsouch parser.error(_("invalid option combined with -c")) 36442536Snsouch 36542475Snsouch (perms, fsname) = mungeargs(2) 36642536Snsouch who = None 36742536Snsouch elif options.everyone: 36842475Snsouch if options.user or options.group or \ 36955939Snsouch options.create or options.set: 37042475Snsouch parser.error(_("invalid option combined with -e")) 37142475Snsouch 37242475Snsouch (perms, fsname) = mungeargs(2) 37342475Snsouch who = ["everyone"] 37442475Snsouch else: 37528257Smsmith (perms, fsname) = mungeargs(3) 37628257Smsmith who = args[0].split(",") 37742475Snsouch 37842475Snsouch if not options.local and not options.descend: 37955939Snsouch options.local = True 38055939Snsouch options.descend = True 38128219Smsmith 382187576Sjhb d = args_to_perms(parser, options, who, perms) 383187576Sjhb 38428257Smsmith ds = zfs.dataset.Dataset(fsname, snaps=False) 385187576Sjhb 386187576Sjhb if not un and perms: 387187576Sjhb for p in perms: 388187576Sjhb if p[0] == "@" and not hasset(ds, p): 389187576Sjhb parser.error(_("set %s is not defined") % p) 390187576Sjhb 391187576Sjhb ds.set_fsacl(un, d) 392187576Sjhb if un and options.recursive: 393187576Sjhb for child in ds.descendents(): 394187576Sjhb child.set_fsacl(un, d) 395187576Sjhb