5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
22 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 # Copyright (c) 2013 by Delphix. All rights reserved.
24 # Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
27 """This module implements the "zfs allow" and "zfs unallow" subcommands.
28 The only public interface is the zfs.allow.do_allow() function."""
40 class FSPerms(object):
41 """This class represents all the permissions that are set on a
42 particular filesystem (not including those inherited)."""
44 __slots__
= "create", "sets", "local", "descend", "ld"
45 __repr__
= zfs
.util
.default_repr
47 def __init__(self
, raw
):
48 """Create a FSPerms based on the dict of raw permissions
49 from zfs.ioctl.get_fsacl()."""
53 # below are { "Ntype name": set(perms) }
54 # where N is a number that we just use for sorting,
55 # type is "user", "group", "everyone", or "" (for sets)
56 # name is a user, group, or set name, or "" (for everyone)
62 # see the comment in dsl_deleg.c for the definition of whokey
63 for whokey
in raw
.keys():
64 perms
= raw
[whokey
].keys()
65 whotypechr
= whokey
[0].lower()
68 self
.create
.update(perms
)
69 elif whotypechr
== "s":
71 self
.sets
.setdefault(nwho
, set()).update(perms
)
75 name
= pwd
.getpwuid(int(ws
)).pw_name
78 nwho
= "1user " + name
79 elif whotypechr
== "g":
81 name
= grp
.getgrgid(int(ws
)).gr_name
84 nwho
= "2group " + name
85 elif whotypechr
== "e":
88 raise ValueError(whotypechr
)
92 elif whokey
[1] == "d":
95 raise ValueError(whokey
[1])
97 d
.setdefault(nwho
, set()).update(perms
)
99 # Find perms that are in both local and descend, and
101 for nwho
in self
.local
:
102 if nwho
not in self
.descend
:
104 # note: these are set operations
105 self
.ld
[nwho
] = self
.local
[nwho
] & self
.descend
[nwho
]
106 self
.local
[nwho
] -= self
.ld
[nwho
]
107 self
.descend
[nwho
] -= self
.ld
[nwho
]
110 def __ldstr(d
, header
):
112 for (nwho
, perms
) in sorted(d
.items()):
113 # local and descend may have entries where perms
114 # is an empty set, due to consolidating all
115 # permissions into ld
118 (nwho
[1:], ",".join(sorted(perms
)))
124 s
= self
.__ldstr
(self
.sets
, _("Permission sets:\n"))
127 s
+= _("Create time permissions:\n")
128 s
+= "\t%s\n" % ",".join(sorted(self
.create
))
130 s
+= self
.__ldstr
(self
.local
, _("Local permissions:\n"))
131 s
+= self
.__ldstr
(self
.descend
, _("Descendent permissions:\n"))
132 s
+= self
.__ldstr
(self
.ld
, _("Local+Descendent permissions:\n"))
135 def args_to_perms(parser
, options
, who
, perms
):
136 """Return a dict of raw perms {"whostr" -> {"perm" -> None}}
137 based on the command-line input."""
139 # perms is not set if we are doing a "zfs unallow <who> <fs>" to
140 # remove all of someone's permissions
142 setperms
= dict(((p
, None) for p
in perms
if p
[0] == "@"))
143 baseperms
= dict(((canonicalized_perm(p
), None)
144 for p
in perms
if p
[0] != "@"))
151 def storeperm(typechr
, inheritchr
, arg
):
152 assert typechr
in "ugecs"
153 assert inheritchr
in "ld-"
156 return "%c%c$%s" % (t
, inheritchr
, arg
)
158 if baseperms
or not perms
:
159 d
[mkwhokey(typechr
)] = baseperms
160 if setperms
or not perms
:
161 d
[mkwhokey(typechr
.upper())] = setperms
163 def decodeid(w
, toidfunc
, fmt
):
168 return toidfunc(w
)[2]
170 parser
.error(fmt
% w
)
173 storeperm("s", "-", who
)
175 storeperm("c", "-", "")
179 id = decodeid(w
, pwd
.getpwnam
,
180 _("invalid user %s"))
183 id = decodeid(w
, grp
.getgrnam
,
184 _("invalid group %s"))
186 elif w
== "everyone":
191 id = pwd
.getpwnam(w
)[2]
195 id = grp
.getgrnam(w
)[2]
198 parser
.error(_("invalid user/group %s") % w
)
200 storeperm(typechr
, "l", id)
202 storeperm(typechr
, "d", id)
206 create
=_("Must also have the 'mount' ability"),
207 destroy
=_("Must also have the 'mount' ability"),
210 clone
=_("""Must also have the 'create' ability and 'mount'
211 \t\t\t\tability in the origin file system"""),
212 promote
=_("""Must also have the 'mount'
213 \t\t\t\tand 'promote' ability in the origin file system"""),
214 rename
=_("""Must also have the 'mount' and 'create'
215 \t\t\t\tability in the new parent"""),
216 receive
=_("Must also have the 'mount' and 'create' ability"),
217 allow
=_("Must also have the permission that is being\n\t\t\t\tallowed"),
218 mount
=_("Allows mount/umount of ZFS datasets"),
219 share
=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"),
221 hold
=_("Allows adding a user hold to a snapshot"),
222 release
=_("Allows releasing a user hold which\n\t\t\t\tmight destroy the snapshot"),
223 diff
=_("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"),
228 userprop
=_("Allows changing any user property"),
229 userquota
=_("Allows accessing any userquota@... property"),
230 groupquota
=_("Allows accessing any groupquota@... property"),
231 userused
=_("Allows reading any userused@... property"),
232 groupused
=_("Allows reading any groupused@... property"),
235 def hasset(ds
, setname
):
236 """Return True if the given setname (string) is defined for this
238 # It would be nice to cache the result of get_fsacl().
239 for raw
in ds
.get_fsacl().values():
240 for whokey
in raw
.keys():
241 if whokey
[0].lower() == "s" and whokey
[3:] == setname
:
245 def canonicalized_perm(permname
):
246 """Return the canonical name (string) for this permission (string).
247 Raises ZFSError if it is not a valid permission."""
248 if permname
in perms_subcmd
.keys() or permname
in perms_other
.keys():
251 return zfs
.dataset
.getpropobj(permname
).name
253 raise zfs
.util
.ZFSError(errno
.EINVAL
, permname
,
254 _("invalid permission"))
257 """Print the set of supported permissions."""
258 print(_("\nThe following permissions are supported:\n"))
259 fmt
= "%-16s %-14s\t%s"
260 print(fmt
% (_("NAME"), _("TYPE"), _("NOTES")))
262 for (name
, note
) in sorted(perms_subcmd
.items()):
263 print(fmt
% (name
, _("subcommand"), note
))
265 for (name
, note
) in sorted(perms_other
.items()):
266 print(fmt
% (name
, _("other"), note
))
268 for (name
, prop
) in sorted(zfs
.dataset
.proptable
.items()):
269 if prop
.visible
and prop
.delegatable():
270 print(fmt
% (name
, _("property"), ""))
273 """Implements the "zfs allow" and "zfs unallow" subcommands."""
274 un
= (sys
.argv
[1] == "unallow")
281 parser
.exit("zfs: error: " + msg
)
286 u
= _("""unallow [-rldug] <"everyone"|user|group>[,...]
287 [<perm|@setname>[,...]] <filesystem|volume>
288 unallow [-rld] -e [<perm|@setname>[,...]] <filesystem|volume>
289 unallow [-r] -c [<perm|@setname>[,...]] <filesystem|volume>
290 unallow [-r] -s @setname [<perm|@setname>[,...]] <filesystem|volume>""")
292 sstr
= _("undefine permission set")
294 u
= _("""allow <filesystem|volume>
295 allow [-ldug] <"everyone"|user|group>[,...] <perm|@setname>[,...]
297 allow [-ld] -e <perm|@setname>[,...] <filesystem|volume>
298 allow -c <perm|@setname>[,...] <filesystem|volume>
299 allow -s @setname <perm|@setname>[,...] <filesystem|volume>""")
301 sstr
= _("define permission set")
303 parser
= optparse
.OptionParser(usage
=u
, prog
="zfs")
305 parser
.add_option("-l", action
="store_true", dest
="local",
306 help=_("%s permission locally") % verb
)
307 parser
.add_option("-d", action
="store_true", dest
="descend",
308 help=_("%s permission for descendents") % verb
)
309 parser
.add_option("-u", action
="store_true", dest
="user",
310 help=_("%s permission for user") % verb
)
311 parser
.add_option("-g", action
="store_true", dest
="group",
312 help=_("%s permission for group") % verb
)
313 parser
.add_option("-e", action
="store_true", dest
="everyone",
314 help=_("%s permission for everyone") % verb
)
315 parser
.add_option("-c", action
="store_true", dest
="create",
316 help=_("%s create time permissions") % verb
)
317 parser
.add_option("-s", action
="store_true", dest
="set", help=sstr
)
319 parser
.add_option("-r", action
="store_true", dest
="recursive",
320 help=_("remove permissions recursively"))
322 if len(sys
.argv
) == 3 and not un
:
323 # just print the permissions on this fs
325 if sys
.argv
[2] == "-h":
326 # hack to make "zfs allow -h" work
328 ds
= zfs
.dataset
.Dataset(sys
.argv
[2], snaps
=False)
331 for (fs
, raw
) in ds
.get_fsacl().items():
334 for fs
in sorted(p
.keys(), reverse
=True):
335 s
= _("---- Permissions on %s ") % fs
336 print(s
+ "-" * (70-len(s
)))
340 (options
, args
) = parser
.parse_args(sys
.argv
[2:])
342 if sum((bool(options
.everyone
), bool(options
.user
),
343 bool(options
.group
))) > 1:
344 parser
.error(_("-u, -g, and -e are mutually exclusive"))
346 def mungeargs(expected_len
):
347 if un
and len(args
) == expected_len
-1:
348 return (None, args
[expected_len
-2])
349 elif len(args
) == expected_len
:
350 return (args
[expected_len
-2].split(","),
351 args
[expected_len
-1])
353 usage(_("wrong number of parameters"))
356 if options
.local
or options
.descend
or options
.user
or \
357 options
.group
or options
.everyone
or options
.create
:
358 parser
.error(_("invalid option combined with -s"))
359 if args
[0][0] != "@":
360 parser
.error(_("invalid set name: missing '@' prefix"))
362 (perms
, fsname
) = mungeargs(3)
365 if options
.local
or options
.descend
or options
.user
or \
366 options
.group
or options
.everyone
or options
.set:
367 parser
.error(_("invalid option combined with -c"))
369 (perms
, fsname
) = mungeargs(2)
371 elif options
.everyone
:
372 if options
.user
or options
.group
or \
373 options
.create
or options
.set:
374 parser
.error(_("invalid option combined with -e"))
376 (perms
, fsname
) = mungeargs(2)
379 (perms
, fsname
) = mungeargs(3)
380 who
= args
[0].split(",")
382 if not options
.local
and not options
.descend
:
384 options
.descend
= True
386 d
= args_to_perms(parser
, options
, who
, perms
)
388 ds
= zfs
.dataset
.Dataset(fsname
, snaps
=False)
392 if p
[0] == "@" and not hasset(ds
, p
):
393 parser
.error(_("set %s is not defined") % p
)
396 if un
and options
.recursive
:
397 for child
in ds
.descendents():
398 child
.set_fsacl(un
, d
)