1#compdef zfs
2# Synced with the S11U1 man page
3
4_zfs() {
5	local context state line expl
6	typeset -A opt_args
7	local -a subcmds rw_properties rw_propnames ro_properties create_properties
8	local -a share_nfs_ro_properties share_nfs_rw_properties
9	local -a share_smb_ro_properties share_nfs_rw_properties
10	local -a share_ro_properties share_rw_properties
11	local -a difffields delegatable_perms
12
13	subcmds=(
14		"create" "destroy" "clone" "promote" "rename" "snapshot"
15		"rollback" "list" "set" "get" "inherit" "mount" "unmount"
16		"share" "unshare" "send" "receive" "allow" "unallow"
17		"upgrade" "userspace" "groupspace" "hold" "holds" "release"
18		"diff" "key" "help"
19	)
20
21	share_nfs_ro_properties=(
22		"share.nfs.all"
23	)
24
25	share_nfs_rw_properties=(
26		"share.nfs:value:(on off)"
27		"share.nfs.aclok:value:(on off)"
28		"share.nfs.acflfab:value:(on off)"
29		"share.nfs.anon:uid:"
30		"share.nfs.charset.euc-cn:access-list:"
31		"share.nfs.charset.euc-jpms:access-list:"
32		"share.nfs.charset.euc-kr:access-list:"
33		"share.nfs.charset.euc-tw:access-list:"
34		"share.nfs.charset.iso8859-1:access-list:"
35		"share.nfs.charset.iso8859-2:access-list:"
36		"share.nfs.charset.iso8859-5:access-list:"
37		"share.nfs.charset.iso8859-6:access-list:"
38		"share.nfs.charset.iso8859-7:access-list:"
39		"share.nfs.charset.iso8859-8:access-list:"
40		"share.nfs.charset.iso8859-9:access-list:"
41		"share.nfs.charset.iso8859-13:access-list:"
42		"share.nfs.charset.iso8859-15:access-list:"
43		"share.nfs.charset.koi8-r:access-list:"
44		"share.nfs.index:file:_files"
45		"share.nfs.log:nfslog.conf tag:"
46		"share.nfs.nosub:value:(on off)"
47		"share.nfs.nosuid:value:(on off)"
48		"share.nfs.public:value:(on off)"
49		"share.nfs.sec:security-mode-list:"
50		"share.nfs.sec.default.none:access-list:"
51		"share.nfs.sec.default.ro:access-list:"
52		"share.nfs.sec.default.root:access-list:"
53		"share.nfs.sec.default.root_mapping:uid:"
54		"share.nfs.sec.default.rw:access-list:"
55		"share.nfs.sec.default.window:seconds"
56		"share.nfs.sec.dh.none:access-list:"
57		"share.nfs.sec.dh.ro:access-list:"
58		"share.nfs.sec.dh.root:access-list:"
59		"share.nfs.sec.dh.root_mapping:uid:"
60		"share.nfs.sec.dh.rw:access-list:"
61		"share.nfs.sec.dh.window:seconds"
62		"share.nfs.sec.krb5.none:access-list:"
63		"share.nfs.sec.krb5.ro:access-list:"
64		"share.nfs.sec.krb5.root:access-list:"
65		"share.nfs.sec.krb5.root_mapping:uid:"
66		"share.nfs.sec.krb5.rw:access-list:"
67		"share.nfs.sec.krb5.window:seconds"
68		"share.nfs.sec.krb5i.none:access-list:"
69		"share.nfs.sec.krb5i.ro:access-list:"
70		"share.nfs.sec.krb5i.root:access-list:"
71		"share.nfs.sec.krb5i.root_mapping:uid:"
72		"share.nfs.sec.krb5i.rw:access-list:"
73		"share.nfs.sec.krb5i.window:seconds"
74		"share.nfs.sec.krb5p.none:access-list:"
75		"share.nfs.sec.krb5p.ro:access-list:"
76		"share.nfs.sec.krb5p.root:access-list:"
77		"share.nfs.sec.krb5p.root_mapping:uid:"
78		"share.nfs.sec.krb5p.rw:access-list:"
79		"share.nfs.sec.krb5p.window:seconds"
80		"share.nfs.sec.none.none:access-list:"
81		"share.nfs.sec.none.ro:access-list:"
82		"share.nfs.sec.none.root:access-list:"
83		"share.nfs.sec.none.root_mapping:uid:"
84		"share.nfs.sec.none.rw:access-list:"
85		"share.nfs.sec.none.window:seconds"
86		"share.nfs.sec.sys.none:access-list:"
87		"share.nfs.sec.sys.ro:access-list:"
88		"share.nfs.sec.sys.root:access-list:"
89		"share.nfs.sec.sys.root_mapping:uid:"
90		"share.nfs.sec.sys.rw:access-list:"
91		"share.nfs.sec.sys.window:seconds"
92	)
93
94	share_smb_ro_properties=(
95		"share.smb.all"
96	)
97
98	share_smb_rw_properties=(
99		"share.smb:value:(on off)"
100		"share.smb.ad-container"
101		"share.smb.abe"
102		"share.smb.csc:value:(disabled manual auto vdo)"
103		"share.smb.catia:value:(on off)"
104		"share.smb.dfsroot:value:(on off)"
105		"share.smb.guestok:value:(on off)"
106		"share.smb.ro:access-list:"
107		"share.smb.rw:access-list:"
108		"share.smb.none:access-list:"
109	)
110
111	share_ro_properties=(
112		"share.all"
113		"share.fs"
114		"share.name"
115		"share.point"
116		"share.protocols"
117		"share.state"
118		$share_nfs_ro_properties
119		$share_smb_ro_properties
120	)
121
122	share_rw_properties=(
123		"share.desc:description:"
124		"share.noauto:value:(on off)"
125		"share.path:path:"
126		$share_nfs_rw_properties
127		$share_smb_rw_properties
128	)
129
130	# TODO: userused@ and groupused@ could have more extensive handling
131	ro_properties=(
132		"name" "type" "creation" "used" "available" "referenced"
133		"compressratio" "mounted" "origin" "usedbychildren"
134		"usedbydataset" "usedbyrefreservation" "usedbysnapshots"
135		"defer_destroy" "userused@" "userrefs" "groupused@"
136		"keychangedate" "keystatus" "rekeydate"
137		$share_ro_properties
138	)
139
140	# TODO: Be cleverer about what values can be set.  Is there any way to
141	# set the sorting for *size properties to false by default?
142	rw_properties=(
143		"aclinherit:value:(discard noallow restricted passthrough passthrough-x)"
144		"aclmode:value:(discard mask passthrough)"
145		"atime:value:(on off)"
146		"canmount:value:(on off noauto)"
147		"checksum:value:(on off fletcher2 fletcher4 sha256 sha256+mac)"
148		"compression:value:(on off lzjb gzip gzip-{1..9} zle)"
149		"copies:value:(1 2 3)"
150		"dedup:value:(on off verify sha256 sha256,verify)"
151		"devices:value:(on off)"
152		"encryption:value:(off on aes128-ccm aes-192-ccm aes-256-ccm aes-128-gcm aes-192-gcm aes-256-gcm)"
153		"exec:value:(on off)"
154		"groupquota@:value:" # TODO: complete group=size|none
155		"keysource:value:_zfs_keysource_props"
156		"logbias:value:(latency throughput)"
157		"mlslabel:value:(none)" # TODO: list sensitivity labels
158		"mountpoint:path, 'legacy', or 'none':{if [[ -prefix /* ]]; then _path_files -/; else _wanted mountpoints expl 'mountpoint (type \"/\" to start completing paths)' compadd legacy none; fi}"
159		"multilevel:value:(on off)"
160		"nbmand:value:(on off)"
161		"primarycache:value:(all none metadata)"
162		"quota:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == quota= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'quota' compadd none; fi}"
163		"readonly:value:(on off)"
164		"recordsize:value:(512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M)"
165		"refquota:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == refquota= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'refquota' compadd none; fi}"
166		"refreservation:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == refreservation= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'refreservation' compadd none; fi}"
167		"reservation:value:{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == reservation= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'reservation' compadd none; fi}"
168		"rstchown:value:(on off)"
169		"secondarycache:value:(all none metadata)"
170		"setuid:value:(on off)"
171		"shadow:value:" # TODO: complete URI|none
172		"share:share properties:"
173		"snapdir:value:(hidden visible)"
174		"sync:value:(standard always disabled)"
175		"userquota@:value:" # TODO: complete user=size|none
176		"version:value:(1 2 3 4 current)"
177		"volsize:value:" # <size>
178		"vscan:value:(on off)"
179		"xattr:value:(on off)"
180		"zoned:value:(on off)"
181		$share_rw_properties
182	)
183
184	create_properties=(
185		$rw_properties
186		"casesensitivity:value:(sensitive insensitive mixed)"
187		"normalization:value:(none formC formD formKC formKD)"
188		"utf8only:value:(on off)"
189		"volblocksize:value:(512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M)"
190	)
191
192	delegatable_perms=(
193		"allow" "clone" "create" "destroy" "diff" "hold" "key"
194		"keychange" "mount" "promote" "receive" "release" "rename"
195		"rollback" "send" "share" "snapshot"
196		"groupused" "userused" "userprop"
197		${create_properties%%:*}
198	)
199
200	rw_propnames=( ${rw_properties%%:*} )
201
202	difffields=(
203		object parent size links linkschange name oldname user group
204		ctime mtime atime crtime
205	)
206
207	if [[ $service == "zfs" ]]; then
208		_arguments -C -A "-*" \
209			'-\?[Help]' \
210			'*::command:->subcmd' && return 0
211
212		if (( CURRENT == 1 )); then
213			_wanted commands expl "zfs subcommand" compadd -a subcmds
214			return
215		fi
216		service="$words[1]"
217		curcontext="${curcontext%:*}=$service:"
218	fi
219
220	case $service in
221	("create")
222		_arguments -A "-*" \
223			'-p[Create parent datasets]' \
224			'-o[Set initial properties]:property:_values -s , "property" $create_properties' \
225			- set1 \
226			':filesystem:_zfs_dataset -t fs -e "parent dataset"' \
227			- set2 \
228			'-s[Create sparse volume]' \
229			'-b[Set volblocksize]:blocksize:' \
230			'-V[Set size]:size:' \
231			':volume:_zfs_dataset -t fs -e "parent dataset"'
232		;;
233
234	("destroy")
235		_arguments -A "-*" \
236			'-r[Recursively destroy all children]' \
237			'-R[Recursively destroy all dependents]' \
238			- set1 \
239			'-d[delete or mark deferred]' \
240			':snapshot:_zfs_dataset -t snap' \
241			- set2 \
242			'-f[Force unmounts]' \
243			':filesystem/volume/snapshot:_zfs_dataset -t fs -t vol'
244		;;
245
246	("snapshot")
247		_arguments -A "-*" \
248			'-r[Recursively snapshot all descendant datasets]' \
249			'-o[Set property]:property:_values -s , "property" $create_properties' \
250			':filesystem/volume:_zfs_dataset -t fs -t vol -S@'
251		;;
252
253	("rollback")
254		_arguments -A "-*" \
255			'-r[Recursively destroy more recent snapshots]' \
256			'-R[Recursively destroy more recent snapshots and clones]' \
257			'-f[Force unmounts]' \
258			':snapshot:_zfs_dataset -t snap'
259		;;
260
261	("clone")
262		# XXX needs to bail if there are no snapshots
263		_arguments -A "-*" \
264			'-p[Create parent datasets]' \
265			'-K[Create encryption key]' \
266			'-o[Set property]:property:_values -s , "property" $create_properties' \
267			':snapshot:_zfs_dataset -t snap' \
268			':filesystem/volume:_zfs_dataset -t fs -e "parent dataset"'
269		;;
270
271	("promote")
272		_arguments \
273			':filesystem:_zfs_dataset -t clone' \
274		;;
275
276	("rename")
277		_arguments -A "-*" \
278			'(-r)-p[Create parent datasets]' \
279			'(-p)-r[Recursively rename snapshots of all descendent datasets]' \
280			':dataset:_zfs_dataset -r1' \
281			':dataset:_zfs_dataset -r2'
282		;;
283
284	("list")
285		_arguments -A "-*" \
286			'-r[Recursively display children]' \
287			'-H[Scripting mode]' \
288			'-d[Depth]:value:' \
289			'-o[Properties to list]:property:_values -s , "property" $ro_properties $rw_propnames' \
290			'*-s[Sort key (ascending)]:property:_values "property" $ro_properties $rw_propnames' \
291			'*-S[Sort key (descending)]:property:_values "property" $ro_properties $rw_propnames' \
292			'-t[Dataset types to list]:dataset type:_values -s , "dataset type" all filesystem snapshot volume' \
293			'*:filesystem/volume/snapshot/path:_zfs_dataset -p'
294		;;
295
296	("set")
297		_arguments \
298			'-r[Recursively apply value]' \
299			':property:_values -s , "property" $rw_properties' \
300			'*:filesystem/volume:_zfs_dataset -t fs -t vol'
301		;;
302
303	("get")
304		_arguments -A "-*" \
305			"-r[Recursively display children's properties]" \
306			'-d[Depth]:value:' \
307			'-H[Scripting mode]' \
308			'-p[Display numbers exactly]' \
309			'-s[Specify sources]:source:_values -s , "source" local default inherited temporary none' \
310			'-o[Specify fields]:field:_values -s , "field" name property value source' \
311			':property:_values -s , "property" $ro_properties $rw_propnames all' \
312			'*:filesystem/volume/snapshot:_zfs_dataset'
313		;;
314
315	("inherit")
316		_arguments -A "-*" \
317			'-r[Recursively inherit property for all children]' \
318			'-S[Revert to received property value]' \
319			':property:_values -s , "property" $ro_properties $rw_properties' \
320			'*:filesystem/volume:_zfs_dataset -t fs -t vol'
321		;;
322
323	("userspace"|"groupspace")
324		_arguments -A "-*" \
325			'-n[Print numeric ID]' \
326			'-i[Translate SID to POSIX ID]' \
327			'-H[Tab-delimited output with no headers]' \
328			'-p[Parseable mode]' \
329			'-o[Properties to list]:property:_values -s , "property" type name used quota' \
330			'*-s[Sort key (ascending)]:property:_values "property" type name used quota' \
331			'*-S[Sort key (descending)]:property:_values "property" type name used quota' \
332			'-t[Types to list]:type:_values -s , "type" all posixuser smbuser posixgroup smbgroup' \
333			'*:filesystem/volume/snapshot:_zfs_dataset'
334		;;
335
336	("mount")
337		_arguments -A "-*" \
338			'-o[Mount options]:mount options:_values -s , "option" {,no}{devices,exec,setuid} ro rw' \
339			'-O[Overlay mount]' \
340			'-v[Report mount progress]' \
341			- set1 \
342			':filesystem:_zfs_dataset -t fs' \
343			- set2 \
344			'-a[Mount all available ZFS filesystems]'
345		;;
346
347	("unmount")
348		_arguments -A "-*" \
349			- set1 \
350			'-f[Force unmount]' \
351			':filesystem:_zfs_dataset -t fs -t mtpt' \
352			- set2 \
353			'-a[Unmount all ZFS filesystems]'
354		;;
355
356	("share")
357		_arguments -A "-*" \
358			- set1 \
359			'-a[Share all available ZFS filesystems]' \
360			- set2 \
361			'-r[Share filesystems recursively]' \
362			':filesystem:_zfs_dataset -t fs' \
363			- set3 \
364			'*-o[Create a share with these properties]:property:_values -w "share properties" $share_rw_properties' \
365			'-u[Create a share without sharing it]' \
366			':filesystem:_zfs_dataset -t fs' \
367			- set4 \
368			':filesystem:_zfs_dataset -t fs -t mtpt -t share'
369		;;
370
371	("unshare")
372		_arguments -A "-*" \
373			- set1 \
374			'-a[Unshare all shared ZFS filesystems]' \
375			- set2 \
376			'-r[Unshare filesystems recursively]' \
377			':filesystem:_zfs_dataset -t fs' \
378			- set3 \
379			':filesystem:_zfs_dataset -t fs -t mtpt -t share'
380		;;
381
382	("send")
383		_arguments -A "-*" \
384			'-b' \
385			'-i[Generate an incremental stream]:snapshot:_zfs_dataset -t snap' \
386			'-D[Perform dedup processing]' \
387			'-p[Send properties]' \
388			'-v[Verbose]' \
389			- set1 \
390			'-I[Generate an incremental stream with intermediary snapshots]:snapshot:_zfs_dataset -t snap' \
391			'-R[Generate a replication stream package]' \
392			':snapshot:_zfs_dataset -t snap' \
393			- set2 \
394			'-c[Create a self-contained stream]' \
395			'-r[Generate a recursive stream package]' \
396			':snapshot:_zfs_dataset -t snap'
397		;;
398
399	("receive")
400		_arguments -A "-*" \
401			'-v[Verbose]' \
402			'-n[Do not receive the stream]' \
403			'-F[Force a rollback if necessary]' \
404			'-u[Filesystem is not mounted]' \
405			'-o[Include property change in the stream]::' \
406			'-x[Exclude property change from the stream]:property:' \
407			- set1 \
408			':filesystem/volume/snapshot:_zfs_dataset' \
409			- set2 \
410			'(-e)-d[Set path prefix from stream, excluding only pool name]' \
411			'(-d)-e[Set path prefix from stream, using last path element]' \
412			'-:filesystem:_zfs_dataset -t fs'
413		;;
414
415	("allow")
416		_arguments -A "-*" \
417			- set1 \
418			':filesystem/volume:_zfs_dataset -t fs -t vol' \
419			- set2 \
420			'(-g)-u[User]:user:_users' \
421			'(-u)-g[Group]:group:_groups' \
422			'-l[Allow for named dataset]' \
423			'-d[Allow for descendent datasets]' \
424			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
425			':filesystem/volume:_zfs_dataset -t fs -t vol' \
426			- set3 \
427			'-e[Everyone]' \
428			'-l[Allow for named dataset]' \
429			'-d[Allow for descendent datasets]' \
430			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
431			':filesystem/volume:_zfs_dataset -t fs -t vol' \
432			- set4 \
433			'-c[Create-time permissions]' \
434			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
435			':filesystem/volume:_zfs_dataset -t fs -t vol' \
436			- set5 \
437			'-s[Define or modify permission sets]' \
438			':setname:' \
439			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
440			':filesystem/volume:_zfs_dataset -t fs -t vol'
441		;;
442
443	("unallow")
444		_arguments -A "-*" \
445			'-r[Recursive removal]' \
446			- set1 \
447			'-s[Remove permissions from or delete a permission set]:permission set:' \
448			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
449			':filesystem/volume:_zfs_dataset -t fs -t vol' \
450			- set2 \
451			'(-g)-u[User]:user:_users' \
452			'(-u)-g[Group]:group:_groups' \
453			'-l[Allow for named dataset]' \
454			'-d[Allow for descendent datasets]' \
455			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
456			':filesystem/volume:_zfs_dataset -t fs -t vol' \
457			- set3 \
458			'-e[Everyone]' \
459			'-l[Allow for named dataset]' \
460			'-d[Allow for descendent datasets]' \
461			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
462			':filesystem/volume:_zfs_dataset -t fs -t vol' \
463			- set4 \
464			'-c[Create-time permissions]' \
465			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
466			':filesystem/volume:_zfs_dataset -t fs -t vol'
467		;;
468
469	("upgrade")
470		_arguments -A "-*" \
471			- set1 \
472			'-v[Verbose]' \
473			- set2 \
474			'-a[Upgrade all filesystems on all pools]' \
475			'-r[Upgrade descendent filesystems, too]' \
476			'-V[Upgrade to specified version]:version:(1 2)' \
477			- set3 \
478			'-r[Upgrade descendent filesystems, too]' \
479			'-V[Upgrade to specified version]:version:(1 2)' \
480			':filesystem:_zfs_dataset -t fs'
481		;;
482
483	("hold")
484		_arguments -A "-*" \
485			'-r[Apply hold recursively]' \
486			':tag:' \
487			':snapshot:_zfs_dataset -t snap'
488		;;
489
490	("holds")
491		_arguments -A "-*" \
492			'-r[List holds recursively]' \
493			':snapshot:_zfs_dataset -t snap'
494		;;
495
496	("release")
497		_arguments -A "-*" \
498			'-r[Release holds recursively]' \
499			':tag:' \
500			':snapshot:_zfs_dataset -t snap'
501		;;
502
503	("diff")
504		_arguments -A "-*" \
505			'-F[Add column for filetype character]' \
506			'-H[Parseable output]' \
507			'-e[Only show new and changed files]' \
508			'*-o[Show fields]:field:_values "field" $difffields' \
509			'-t[Add column for ctime]' \
510			- set1 \
511			':snapshot:_zfs_dataset -t snap' \
512			':snapshot or filesystem:_zfs_dataset -t snap -t fs' \
513			- set2 \
514			'-E[Show difference from empty]' \
515			':snapshot or filesystem:_zfs_dataset -t snap -t fs'
516		;;
517
518	("key")
519		_arguments -A "-*" \
520			- set1 \
521			'-a[Apply to all datasets in all pools]' \
522			'(-u -K -f)-l[Load the encryption key]' \
523			'(-l -K)-u[Unload the encryption key]' \
524			'(-l -u -f)-K[Create a new data encryption key]' \
525			'(-l -K)-f[Unmount the dataset before unloading the encryption key]' \
526			'-r[Apply recursively]' \
527			':filesystem or volume:_zfs_dataset -t fs -t vol' \
528			- set2 \
529			'-c[Change the encryption key]' \
530			'-o[Change a property]:property:_zfs_keysource_props' \
531			':filesystem or volume:_zfs_dataset -t fs -t vol'
532		;;
533
534	("help")
535		_arguments -A "-*" \
536			- set1 \
537			':command:($subcmds $delegatable_perms $ro_properties ${rw_properties%%:*} properties)' \
538			- set2 \
539			'-l[Display property information]' \
540			': :(properties)'
541		;;
542
543	(*)
544		_message "unknown zfs subcommand: $service"
545		;;
546	esac
547}
548
549_zfs "$@"
550