Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/cddl/contrib/opensolaris/lib/pyzfs/common/allow.py
39563 views
1
#! /usr/bin/python2.6
2
#
3
# CDDL HEADER START
4
#
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.
8
#
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.
13
#
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]
19
#
20
# CDDL HEADER END
21
#
22
# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23
# Copyright (c) 2013 by Delphix. All rights reserved.
24
#
25
26
"""This module implements the "zfs allow" and "zfs unallow" subcommands.
27
The only public interface is the zfs.allow.do_allow() function."""
28
29
import zfs.util
30
import zfs.dataset
31
import optparse
32
import sys
33
import pwd
34
import grp
35
import errno
36
37
_ = zfs.util._
38
39
class FSPerms(object):
40
"""This class represents all the permissions that are set on a
41
particular filesystem (not including those inherited)."""
42
43
__slots__ = "create", "sets", "local", "descend", "ld"
44
__repr__ = zfs.util.default_repr
45
46
def __init__(self, raw):
47
"""Create a FSPerms based on the dict of raw permissions
48
from zfs.ioctl.get_fsacl()."""
49
# set of perms
50
self.create = set()
51
52
# below are { "Ntype name": set(perms) }
53
# where N is a number that we just use for sorting,
54
# type is "user", "group", "everyone", or "" (for sets)
55
# name is a user, group, or set name, or "" (for everyone)
56
self.sets = dict()
57
self.local = dict()
58
self.descend = dict()
59
self.ld = dict()
60
61
# see the comment in dsl_deleg.c for the definition of whokey
62
for whokey in raw.keys():
63
perms = raw[whokey].keys()
64
whotypechr = whokey[0].lower()
65
ws = whokey[3:]
66
if whotypechr == "c":
67
self.create.update(perms)
68
elif whotypechr == "s":
69
nwho = "1" + ws
70
self.sets.setdefault(nwho, set()).update(perms)
71
else:
72
if whotypechr == "u":
73
try:
74
name = pwd.getpwuid(int(ws)).pw_name
75
except KeyError:
76
name = ws
77
nwho = "1user " + name
78
elif whotypechr == "g":
79
try:
80
name = grp.getgrgid(int(ws)).gr_name
81
except KeyError:
82
name = ws
83
nwho = "2group " + name
84
elif whotypechr == "e":
85
nwho = "3everyone"
86
else:
87
raise ValueError(whotypechr)
88
89
if whokey[1] == "l":
90
d = self.local
91
elif whokey[1] == "d":
92
d = self.descend
93
else:
94
raise ValueError(whokey[1])
95
96
d.setdefault(nwho, set()).update(perms)
97
98
# Find perms that are in both local and descend, and
99
# move them to ld.
100
for nwho in self.local:
101
if nwho not in self.descend:
102
continue
103
# note: these are set operations
104
self.ld[nwho] = self.local[nwho] & self.descend[nwho]
105
self.local[nwho] -= self.ld[nwho]
106
self.descend[nwho] -= self.ld[nwho]
107
108
@staticmethod
109
def __ldstr(d, header):
110
s = ""
111
for (nwho, perms) in sorted(d.items()):
112
# local and descend may have entries where perms
113
# is an empty set, due to consolidating all
114
# permissions into ld
115
if perms:
116
s += "\t%s %s\n" % \
117
(nwho[1:], ",".join(sorted(perms)))
118
if s:
119
s = header + s
120
return s
121
122
def __str__(self):
123
s = self.__ldstr(self.sets, _("Permission sets:\n"))
124
125
if self.create:
126
s += _("Create time permissions:\n")
127
s += "\t%s\n" % ",".join(sorted(self.create))
128
129
s += self.__ldstr(self.local, _("Local permissions:\n"))
130
s += self.__ldstr(self.descend, _("Descendent permissions:\n"))
131
s += self.__ldstr(self.ld, _("Local+Descendent permissions:\n"))
132
return s.rstrip()
133
134
def args_to_perms(parser, options, who, perms):
135
"""Return a dict of raw perms {"whostr" -> {"perm" -> None}}
136
based on the command-line input."""
137
138
# perms is not set if we are doing a "zfs unallow <who> <fs>" to
139
# remove all of someone's permissions
140
if perms:
141
setperms = dict(((p, None) for p in perms if p[0] == "@"))
142
baseperms = dict(((canonicalized_perm(p), None)
143
for p in perms if p[0] != "@"))
144
else:
145
setperms = None
146
baseperms = None
147
148
d = dict()
149
150
def storeperm(typechr, inheritchr, arg):
151
assert typechr in "ugecs"
152
assert inheritchr in "ld-"
153
154
def mkwhokey(t):
155
return "%c%c$%s" % (t, inheritchr, arg)
156
157
if baseperms or not perms:
158
d[mkwhokey(typechr)] = baseperms
159
if setperms or not perms:
160
d[mkwhokey(typechr.upper())] = setperms
161
162
def decodeid(w, toidfunc, fmt):
163
try:
164
return int(w)
165
except ValueError:
166
try:
167
return toidfunc(w)[2]
168
except KeyError:
169
parser.error(fmt % w)
170
171
if options.set:
172
storeperm("s", "-", who)
173
elif options.create:
174
storeperm("c", "-", "")
175
else:
176
for w in who:
177
if options.user:
178
id = decodeid(w, pwd.getpwnam,
179
_("invalid user %s"))
180
typechr = "u"
181
elif options.group:
182
id = decodeid(w, grp.getgrnam,
183
_("invalid group %s"))
184
typechr = "g"
185
elif w == "everyone":
186
id = ""
187
typechr = "e"
188
else:
189
try:
190
id = pwd.getpwnam(w)[2]
191
typechr = "u"
192
except KeyError:
193
try:
194
id = grp.getgrnam(w)[2]
195
typechr = "g"
196
except KeyError:
197
parser.error(_("invalid user/group %s") % w)
198
if options.local:
199
storeperm(typechr, "l", id)
200
if options.descend:
201
storeperm(typechr, "d", id)
202
return d
203
204
perms_subcmd = dict(
205
create=_("Must also have the 'mount' ability"),
206
destroy=_("Must also have the 'mount' ability"),
207
snapshot="",
208
rollback="",
209
clone=_("""Must also have the 'create' ability and 'mount'
210
\t\t\t\tability in the origin file system"""),
211
promote=_("""Must also have the 'mount'
212
\t\t\t\tand 'promote' ability in the origin file system"""),
213
rename=_("""Must also have the 'mount' and 'create'
214
\t\t\t\tability in the new parent"""),
215
receive=_("Must also have the 'mount' and 'create' ability"),
216
allow=_("Must also have the permission that is being\n\t\t\t\tallowed"),
217
mount=_("Allows mount/umount of ZFS datasets"),
218
share=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"),
219
send="",
220
hold=_("Allows adding a user hold to a snapshot"),
221
release=_("Allows releasing a user hold which\n\t\t\t\tmight destroy the snapshot"),
222
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"),
223
bookmark="",
224
)
225
226
perms_other = dict(
227
userprop=_("Allows changing any user property"),
228
userquota=_("Allows accessing any userquota@... property"),
229
groupquota=_("Allows accessing any groupquota@... property"),
230
userused=_("Allows reading any userused@... property"),
231
groupused=_("Allows reading any groupused@... property"),
232
)
233
234
def hasset(ds, setname):
235
"""Return True if the given setname (string) is defined for this
236
ds (Dataset)."""
237
# It would be nice to cache the result of get_fsacl().
238
for raw in ds.get_fsacl().values():
239
for whokey in raw.keys():
240
if whokey[0].lower() == "s" and whokey[3:] == setname:
241
return True
242
return False
243
244
def canonicalized_perm(permname):
245
"""Return the canonical name (string) for this permission (string).
246
Raises ZFSError if it is not a valid permission."""
247
if permname in perms_subcmd.keys() or permname in perms_other.keys():
248
return permname
249
try:
250
return zfs.dataset.getpropobj(permname).name
251
except KeyError:
252
raise zfs.util.ZFSError(errno.EINVAL, permname,
253
_("invalid permission"))
254
255
def print_perms():
256
"""Print the set of supported permissions."""
257
print(_("\nThe following permissions are supported:\n"))
258
fmt = "%-16s %-14s\t%s"
259
print(fmt % (_("NAME"), _("TYPE"), _("NOTES")))
260
261
for (name, note) in sorted(perms_subcmd.iteritems()):
262
print(fmt % (name, _("subcommand"), note))
263
264
for (name, note) in sorted(perms_other.iteritems()):
265
print(fmt % (name, _("other"), note))
266
267
for (name, prop) in sorted(zfs.dataset.proptable.iteritems()):
268
if prop.visible and prop.delegatable():
269
print(fmt % (name, _("property"), ""))
270
271
def do_allow():
272
"""Implements the "zfs allow" and "zfs unallow" subcommands."""
273
un = (sys.argv[1] == "unallow")
274
275
def usage(msg=None):
276
parser.print_help()
277
print_perms()
278
if msg:
279
print
280
parser.exit("zfs: error: " + msg)
281
else:
282
parser.exit()
283
284
if un:
285
u = _("""unallow [-rldug] <"everyone"|user|group>[,...]
286
[<perm|@setname>[,...]] <filesystem|volume>
287
unallow [-rld] -e [<perm|@setname>[,...]] <filesystem|volume>
288
unallow [-r] -c [<perm|@setname>[,...]] <filesystem|volume>
289
unallow [-r] -s @setname [<perm|@setname>[,...]] <filesystem|volume>""")
290
verb = _("remove")
291
sstr = _("undefine permission set")
292
else:
293
u = _("""allow <filesystem|volume>
294
allow [-ldug] <"everyone"|user|group>[,...] <perm|@setname>[,...]
295
<filesystem|volume>
296
allow [-ld] -e <perm|@setname>[,...] <filesystem|volume>
297
allow -c <perm|@setname>[,...] <filesystem|volume>
298
allow -s @setname <perm|@setname>[,...] <filesystem|volume>""")
299
verb = _("set")
300
sstr = _("define permission set")
301
302
parser = optparse.OptionParser(usage=u, prog="zfs")
303
304
parser.add_option("-l", action="store_true", dest="local",
305
help=_("%s permission locally") % verb)
306
parser.add_option("-d", action="store_true", dest="descend",
307
help=_("%s permission for descendents") % verb)
308
parser.add_option("-u", action="store_true", dest="user",
309
help=_("%s permission for user") % verb)
310
parser.add_option("-g", action="store_true", dest="group",
311
help=_("%s permission for group") % verb)
312
parser.add_option("-e", action="store_true", dest="everyone",
313
help=_("%s permission for everyone") % verb)
314
parser.add_option("-c", action="store_true", dest="create",
315
help=_("%s create time permissions") % verb)
316
parser.add_option("-s", action="store_true", dest="set", help=sstr)
317
if un:
318
parser.add_option("-r", action="store_true", dest="recursive",
319
help=_("remove permissions recursively"))
320
321
if len(sys.argv) == 3 and not un:
322
# just print the permissions on this fs
323
324
if sys.argv[2] == "-h":
325
# hack to make "zfs allow -h" work
326
usage()
327
ds = zfs.dataset.Dataset(sys.argv[2], snaps=False)
328
329
p = dict()
330
for (fs, raw) in ds.get_fsacl().items():
331
p[fs] = FSPerms(raw)
332
333
for fs in sorted(p.keys(), reverse=True):
334
s = _("---- Permissions on %s ") % fs
335
print(s + "-" * (70-len(s)))
336
print(p[fs])
337
return
338
339
340
(options, args) = parser.parse_args(sys.argv[2:])
341
342
if sum((bool(options.everyone), bool(options.user),
343
bool(options.group))) > 1:
344
parser.error(_("-u, -g, and -e are mutually exclusive"))
345
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])
352
else:
353
usage(_("wrong number of parameters"))
354
355
if options.set:
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"))
361
362
(perms, fsname) = mungeargs(3)
363
who = args[0]
364
elif options.create:
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"))
368
369
(perms, fsname) = mungeargs(2)
370
who = None
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"))
375
376
(perms, fsname) = mungeargs(2)
377
who = ["everyone"]
378
else:
379
(perms, fsname) = mungeargs(3)
380
who = args[0].split(",")
381
382
if not options.local and not options.descend:
383
options.local = True
384
options.descend = True
385
386
d = args_to_perms(parser, options, who, perms)
387
388
ds = zfs.dataset.Dataset(fsname, snaps=False)
389
390
if not un and perms:
391
for p in perms:
392
if p[0] == "@" and not hasset(ds, p):
393
parser.error(_("set %s is not defined") % p)
394
395
ds.set_fsacl(un, d)
396
if un and options.recursive:
397
for child in ds.descendents():
398
child.set_fsacl(un, d)
399
400