Path: blob/main/cddl/contrib/opensolaris/lib/pyzfs/common/allow.py
39563 views
#! /usr/bin/python2.61#2# CDDL HEADER START3#4# The contents of this file are subject to the terms of the5# Common Development and Distribution License (the "License").6# You may not use this file except in compliance with the License.7#8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9# or http://www.opensolaris.org/os/licensing.10# See the License for the specific language governing permissions11# and limitations under the License.12#13# When distributing Covered Code, include this CDDL HEADER in each14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.15# If applicable, add the following below this CDDL HEADER, with the16# fields enclosed by brackets "[]" replaced with your own identifying17# information: Portions Copyright [yyyy] [name of copyright owner]18#19# CDDL HEADER END20#21# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.22# Copyright (c) 2013 by Delphix. All rights reserved.23#2425"""This module implements the "zfs allow" and "zfs unallow" subcommands.26The only public interface is the zfs.allow.do_allow() function."""2728import zfs.util29import zfs.dataset30import optparse31import sys32import pwd33import grp34import errno3536_ = zfs.util._3738class FSPerms(object):39"""This class represents all the permissions that are set on a40particular filesystem (not including those inherited)."""4142__slots__ = "create", "sets", "local", "descend", "ld"43__repr__ = zfs.util.default_repr4445def __init__(self, raw):46"""Create a FSPerms based on the dict of raw permissions47from zfs.ioctl.get_fsacl()."""48# set of perms49self.create = set()5051# below are { "Ntype name": set(perms) }52# where N is a number that we just use for sorting,53# type is "user", "group", "everyone", or "" (for sets)54# name is a user, group, or set name, or "" (for everyone)55self.sets = dict()56self.local = dict()57self.descend = dict()58self.ld = dict()5960# see the comment in dsl_deleg.c for the definition of whokey61for whokey in raw.keys():62perms = raw[whokey].keys()63whotypechr = whokey[0].lower()64ws = whokey[3:]65if whotypechr == "c":66self.create.update(perms)67elif whotypechr == "s":68nwho = "1" + ws69self.sets.setdefault(nwho, set()).update(perms)70else:71if whotypechr == "u":72try:73name = pwd.getpwuid(int(ws)).pw_name74except KeyError:75name = ws76nwho = "1user " + name77elif whotypechr == "g":78try:79name = grp.getgrgid(int(ws)).gr_name80except KeyError:81name = ws82nwho = "2group " + name83elif whotypechr == "e":84nwho = "3everyone"85else:86raise ValueError(whotypechr)8788if whokey[1] == "l":89d = self.local90elif whokey[1] == "d":91d = self.descend92else:93raise ValueError(whokey[1])9495d.setdefault(nwho, set()).update(perms)9697# Find perms that are in both local and descend, and98# move them to ld.99for nwho in self.local:100if nwho not in self.descend:101continue102# note: these are set operations103self.ld[nwho] = self.local[nwho] & self.descend[nwho]104self.local[nwho] -= self.ld[nwho]105self.descend[nwho] -= self.ld[nwho]106107@staticmethod108def __ldstr(d, header):109s = ""110for (nwho, perms) in sorted(d.items()):111# local and descend may have entries where perms112# is an empty set, due to consolidating all113# permissions into ld114if perms:115s += "\t%s %s\n" % \116(nwho[1:], ",".join(sorted(perms)))117if s:118s = header + s119return s120121def __str__(self):122s = self.__ldstr(self.sets, _("Permission sets:\n"))123124if self.create:125s += _("Create time permissions:\n")126s += "\t%s\n" % ",".join(sorted(self.create))127128s += self.__ldstr(self.local, _("Local permissions:\n"))129s += self.__ldstr(self.descend, _("Descendent permissions:\n"))130s += self.__ldstr(self.ld, _("Local+Descendent permissions:\n"))131return s.rstrip()132133def args_to_perms(parser, options, who, perms):134"""Return a dict of raw perms {"whostr" -> {"perm" -> None}}135based on the command-line input."""136137# perms is not set if we are doing a "zfs unallow <who> <fs>" to138# remove all of someone's permissions139if perms:140setperms = dict(((p, None) for p in perms if p[0] == "@"))141baseperms = dict(((canonicalized_perm(p), None)142for p in perms if p[0] != "@"))143else:144setperms = None145baseperms = None146147d = dict()148149def storeperm(typechr, inheritchr, arg):150assert typechr in "ugecs"151assert inheritchr in "ld-"152153def mkwhokey(t):154return "%c%c$%s" % (t, inheritchr, arg)155156if baseperms or not perms:157d[mkwhokey(typechr)] = baseperms158if setperms or not perms:159d[mkwhokey(typechr.upper())] = setperms160161def decodeid(w, toidfunc, fmt):162try:163return int(w)164except ValueError:165try:166return toidfunc(w)[2]167except KeyError:168parser.error(fmt % w)169170if options.set:171storeperm("s", "-", who)172elif options.create:173storeperm("c", "-", "")174else:175for w in who:176if options.user:177id = decodeid(w, pwd.getpwnam,178_("invalid user %s"))179typechr = "u"180elif options.group:181id = decodeid(w, grp.getgrnam,182_("invalid group %s"))183typechr = "g"184elif w == "everyone":185id = ""186typechr = "e"187else:188try:189id = pwd.getpwnam(w)[2]190typechr = "u"191except KeyError:192try:193id = grp.getgrnam(w)[2]194typechr = "g"195except KeyError:196parser.error(_("invalid user/group %s") % w)197if options.local:198storeperm(typechr, "l", id)199if options.descend:200storeperm(typechr, "d", id)201return d202203perms_subcmd = dict(204create=_("Must also have the 'mount' ability"),205destroy=_("Must also have the 'mount' ability"),206snapshot="",207rollback="",208clone=_("""Must also have the 'create' ability and 'mount'209\t\t\t\tability in the origin file system"""),210promote=_("""Must also have the 'mount'211\t\t\t\tand 'promote' ability in the origin file system"""),212rename=_("""Must also have the 'mount' and 'create'213\t\t\t\tability in the new parent"""),214receive=_("Must also have the 'mount' and 'create' ability"),215allow=_("Must also have the permission that is being\n\t\t\t\tallowed"),216mount=_("Allows mount/umount of ZFS datasets"),217share=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"),218send="",219hold=_("Allows adding a user hold to a snapshot"),220release=_("Allows releasing a user hold which\n\t\t\t\tmight destroy the snapshot"),221diff=_("Allows lookup of paths within a dataset,\n\t\t\t\tgiven an object number. Ordinary users need this\n\t\t\t\tin order to use zfs diff"),222bookmark="",223)224225perms_other = dict(226userprop=_("Allows changing any user property"),227userquota=_("Allows accessing any userquota@... property"),228groupquota=_("Allows accessing any groupquota@... property"),229userused=_("Allows reading any userused@... property"),230groupused=_("Allows reading any groupused@... property"),231)232233def hasset(ds, setname):234"""Return True if the given setname (string) is defined for this235ds (Dataset)."""236# It would be nice to cache the result of get_fsacl().237for raw in ds.get_fsacl().values():238for whokey in raw.keys():239if whokey[0].lower() == "s" and whokey[3:] == setname:240return True241return False242243def canonicalized_perm(permname):244"""Return the canonical name (string) for this permission (string).245Raises ZFSError if it is not a valid permission."""246if permname in perms_subcmd.keys() or permname in perms_other.keys():247return permname248try:249return zfs.dataset.getpropobj(permname).name250except KeyError:251raise zfs.util.ZFSError(errno.EINVAL, permname,252_("invalid permission"))253254def print_perms():255"""Print the set of supported permissions."""256print(_("\nThe following permissions are supported:\n"))257fmt = "%-16s %-14s\t%s"258print(fmt % (_("NAME"), _("TYPE"), _("NOTES")))259260for (name, note) in sorted(perms_subcmd.iteritems()):261print(fmt % (name, _("subcommand"), note))262263for (name, note) in sorted(perms_other.iteritems()):264print(fmt % (name, _("other"), note))265266for (name, prop) in sorted(zfs.dataset.proptable.iteritems()):267if prop.visible and prop.delegatable():268print(fmt % (name, _("property"), ""))269270def do_allow():271"""Implements the "zfs allow" and "zfs unallow" subcommands."""272un = (sys.argv[1] == "unallow")273274def usage(msg=None):275parser.print_help()276print_perms()277if msg:278279parser.exit("zfs: error: " + msg)280else:281parser.exit()282283if un:284u = _("""unallow [-rldug] <"everyone"|user|group>[,...]285[<perm|@setname>[,...]] <filesystem|volume>286unallow [-rld] -e [<perm|@setname>[,...]] <filesystem|volume>287unallow [-r] -c [<perm|@setname>[,...]] <filesystem|volume>288unallow [-r] -s @setname [<perm|@setname>[,...]] <filesystem|volume>""")289verb = _("remove")290sstr = _("undefine permission set")291else:292u = _("""allow <filesystem|volume>293allow [-ldug] <"everyone"|user|group>[,...] <perm|@setname>[,...]294<filesystem|volume>295allow [-ld] -e <perm|@setname>[,...] <filesystem|volume>296allow -c <perm|@setname>[,...] <filesystem|volume>297allow -s @setname <perm|@setname>[,...] <filesystem|volume>""")298verb = _("set")299sstr = _("define permission set")300301parser = optparse.OptionParser(usage=u, prog="zfs")302303parser.add_option("-l", action="store_true", dest="local",304help=_("%s permission locally") % verb)305parser.add_option("-d", action="store_true", dest="descend",306help=_("%s permission for descendents") % verb)307parser.add_option("-u", action="store_true", dest="user",308help=_("%s permission for user") % verb)309parser.add_option("-g", action="store_true", dest="group",310help=_("%s permission for group") % verb)311parser.add_option("-e", action="store_true", dest="everyone",312help=_("%s permission for everyone") % verb)313parser.add_option("-c", action="store_true", dest="create",314help=_("%s create time permissions") % verb)315parser.add_option("-s", action="store_true", dest="set", help=sstr)316if un:317parser.add_option("-r", action="store_true", dest="recursive",318help=_("remove permissions recursively"))319320if len(sys.argv) == 3 and not un:321# just print the permissions on this fs322323if sys.argv[2] == "-h":324# hack to make "zfs allow -h" work325usage()326ds = zfs.dataset.Dataset(sys.argv[2], snaps=False)327328p = dict()329for (fs, raw) in ds.get_fsacl().items():330p[fs] = FSPerms(raw)331332for fs in sorted(p.keys(), reverse=True):333s = _("---- Permissions on %s ") % fs334print(s + "-" * (70-len(s)))335print(p[fs])336return337338339(options, args) = parser.parse_args(sys.argv[2:])340341if sum((bool(options.everyone), bool(options.user),342bool(options.group))) > 1:343parser.error(_("-u, -g, and -e are mutually exclusive"))344345def mungeargs(expected_len):346if un and len(args) == expected_len-1:347return (None, args[expected_len-2])348elif len(args) == expected_len:349return (args[expected_len-2].split(","),350args[expected_len-1])351else:352usage(_("wrong number of parameters"))353354if options.set:355if options.local or options.descend or options.user or \356options.group or options.everyone or options.create:357parser.error(_("invalid option combined with -s"))358if args[0][0] != "@":359parser.error(_("invalid set name: missing '@' prefix"))360361(perms, fsname) = mungeargs(3)362who = args[0]363elif options.create:364if options.local or options.descend or options.user or \365options.group or options.everyone or options.set:366parser.error(_("invalid option combined with -c"))367368(perms, fsname) = mungeargs(2)369who = None370elif options.everyone:371if options.user or options.group or \372options.create or options.set:373parser.error(_("invalid option combined with -e"))374375(perms, fsname) = mungeargs(2)376who = ["everyone"]377else:378(perms, fsname) = mungeargs(3)379who = args[0].split(",")380381if not options.local and not options.descend:382options.local = True383options.descend = True384385d = args_to_perms(parser, options, who, perms)386387ds = zfs.dataset.Dataset(fsname, snaps=False)388389if not un and perms:390for p in perms:391if p[0] == "@" and not hasset(ds, p):392parser.error(_("set %s is not defined") % p)393394ds.set_fsacl(un, d)395if un and options.recursive:396for child in ds.descendents():397child.set_fsacl(un, d)398399400