Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/cddl/contrib/opensolaris/lib/pyzfs/common/userspace.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
#
24
25
"""This module implements the "zfs userspace" and "zfs groupspace" subcommands.
26
The only public interface is the zfs.userspace.do_userspace() function."""
27
28
import optparse
29
import sys
30
import pwd
31
import grp
32
import errno
33
import solaris.misc
34
import zfs.util
35
import zfs.ioctl
36
import zfs.dataset
37
import zfs.table
38
39
_ = zfs.util._
40
41
# map from property name prefix -> (field name, isgroup)
42
props = {
43
"userused@": ("used", False),
44
"userquota@": ("quota", False),
45
"groupused@": ("used", True),
46
"groupquota@": ("quota", True),
47
}
48
49
def skiptype(options, prop):
50
"""Return True if this property (eg "userquota@") should be skipped."""
51
(field, isgroup) = props[prop]
52
if field not in options.fields:
53
return True
54
if isgroup and "posixgroup" not in options.types and \
55
"smbgroup" not in options.types:
56
return True
57
if not isgroup and "posixuser" not in options.types and \
58
"smbuser" not in options.types:
59
return True
60
return False
61
62
def new_entry(options, isgroup, domain, rid):
63
"""Return a dict("field": value) for this domain (string) + rid (int)"""
64
65
if domain:
66
idstr = "%s-%u" % (domain, rid)
67
else:
68
idstr = "%u" % rid
69
70
(typename, mapfunc) = {
71
(1, 1): ("SMB Group", lambda id: solaris.misc.sid_to_name(id, 0)),
72
(1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name),
73
(0, 1): ("SMB User", lambda id: solaris.misc.sid_to_name(id, 1)),
74
(0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name)
75
}[isgroup, bool(domain)]
76
77
if typename.lower().replace(" ", "") not in options.types:
78
return None
79
80
v = dict()
81
v["type"] = typename
82
83
# python's getpwuid/getgrgid is confused by ephemeral uids
84
if not options.noname and rid < 1<<31:
85
try:
86
v["name"] = mapfunc(idstr)
87
except KeyError:
88
pass
89
90
if "name" not in v:
91
v["name"] = idstr
92
if not domain:
93
# it's just a number, so pad it with spaces so
94
# that it will sort numerically
95
v["name.sort"] = "%20d" % rid
96
# fill in default values
97
v["used"] = "0"
98
v["used.sort"] = 0
99
v["quota"] = "none"
100
v["quota.sort"] = 0
101
return v
102
103
def process_one_raw(acct, options, prop, elem):
104
"""Update the acct dict to incorporate the
105
information from this elem from Dataset.userspace(prop)."""
106
107
(domain, rid, value) = elem
108
(field, isgroup) = props[prop]
109
110
if options.translate and domain:
111
try:
112
rid = solaris.misc.sid_to_id("%s-%u" % (domain, rid),
113
not isgroup)
114
domain = None
115
except KeyError:
116
pass;
117
key = (isgroup, domain, rid)
118
119
try:
120
v = acct[key]
121
except KeyError:
122
v = new_entry(options, isgroup, domain, rid)
123
if not v:
124
return
125
acct[key] = v
126
127
# Add our value to an existing value, which may be present if
128
# options.translate is set.
129
value = v[field + ".sort"] = value + v[field + ".sort"]
130
131
if options.parsable:
132
v[field] = str(value)
133
else:
134
v[field] = zfs.util.nicenum(value)
135
136
def do_userspace():
137
"""Implements the "zfs userspace" and "zfs groupspace" subcommands."""
138
139
def usage(msg=None):
140
parser.print_help()
141
if msg:
142
print
143
parser.exit("zfs: error: " + msg)
144
else:
145
parser.exit()
146
147
if sys.argv[1] == "userspace":
148
defaulttypes = "posixuser,smbuser"
149
else:
150
defaulttypes = "posixgroup,smbgroup"
151
152
fields = ("type", "name", "used", "quota")
153
rjustfields = ("used", "quota")
154
types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup")
155
156
u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1]
157
u += _(" [-t type[,...]] <filesystem|snapshot>")
158
parser = optparse.OptionParser(usage=u, prog="zfs")
159
160
parser.add_option("-n", action="store_true", dest="noname",
161
help=_("Print numeric ID instead of user/group name"))
162
parser.add_option("-i", action="store_true", dest="translate",
163
help=_("translate SID to posix (possibly ephemeral) ID"))
164
parser.add_option("-H", action="store_true", dest="noheaders",
165
help=_("no headers, tab delimited output"))
166
parser.add_option("-p", action="store_true", dest="parsable",
167
help=_("exact (parsable) numeric output"))
168
parser.add_option("-o", dest="fields", metavar="field[,...]",
169
default="type,name,used,quota",
170
help=_("print only these fields (eg type,name,used,quota)"))
171
parser.add_option("-s", dest="sortfields", metavar="field",
172
type="choice", choices=fields, default=list(),
173
action="callback", callback=zfs.util.append_with_opt,
174
help=_("sort field"))
175
parser.add_option("-S", dest="sortfields", metavar="field",
176
type="choice", choices=fields, #-s sets the default
177
action="callback", callback=zfs.util.append_with_opt,
178
help=_("reverse sort field"))
179
parser.add_option("-t", dest="types", metavar="type[,...]",
180
default=defaulttypes,
181
help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)"))
182
183
(options, args) = parser.parse_args(sys.argv[2:])
184
if len(args) != 1:
185
usage(_("wrong number of arguments"))
186
dsname = args[0]
187
188
options.fields = options.fields.split(",")
189
for f in options.fields:
190
if f not in fields:
191
usage(_("invalid field %s") % f)
192
193
options.types = options.types.split(",")
194
for t in options.types:
195
if t not in types:
196
usage(_("invalid type %s") % t)
197
198
if not options.sortfields:
199
options.sortfields = [("-s", "type"), ("-s", "name")]
200
201
if "all" in options.types:
202
options.types = types[1:]
203
204
ds = zfs.dataset.Dataset(dsname, types=("filesystem"))
205
206
if ds.getprop("jailed") and solaris.misc.isglobalzone():
207
options.noname = True
208
209
if not ds.getprop("useraccounting"):
210
print(_("Initializing accounting information on old filesystem, please wait..."))
211
ds.userspace_upgrade()
212
213
# gather and process accounting information
214
# Due to -i, we need to keep a dict, so we can potentially add
215
# together the posix ID and SID's usage. Grr.
216
acct = dict()
217
for prop in props.keys():
218
if skiptype(options, prop):
219
continue;
220
for elem in ds.userspace(prop):
221
process_one_raw(acct, options, prop, elem)
222
223
def cmpkey(val):
224
l = list()
225
for (opt, field) in options.sortfields:
226
try:
227
n = val[field + ".sort"]
228
except KeyError:
229
n = val[field]
230
if opt == "-S":
231
# reverse sorting
232
try:
233
n = -n
234
except TypeError:
235
# it's a string; decompose it
236
# into an array of integers,
237
# each one the negative of that
238
# character
239
n = [-ord(c) for c in n]
240
l.append(n)
241
return l
242
243
t = zfs.table.Table(options.fields, rjustfields)
244
for val in acct.itervalues():
245
t.addline(cmpkey(val), val)
246
t.printme(not options.noheaders)
247
248