1/*-
2 * Copyright (c) 1999-2006 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * This software was developed by Robert Watson for the TrustedBSD Project.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28/*
29 * Developed by the TrustedBSD Project.
30 *
31 * ACL support routines specific to POSIX.1e access control lists.  These are
32 * utility routines for code common across file systems implementing POSIX.1e
33 * ACLs.
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39#include <sys/param.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/systm.h>
43#include <sys/mount.h>
44#include <sys/priv.h>
45#include <sys/vnode.h>
46#include <sys/errno.h>
47#include <sys/stat.h>
48#include <sys/acl.h>
49
50/*
51 * Implement a version of vaccess() that understands POSIX.1e ACL semantics;
52 * the access ACL has already been prepared for evaluation by the file system
53 * and is passed via 'uid', 'gid', and 'acl'.  Return 0 on success, else an
54 * errno value.
55 */
56int
57vaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid,
58    struct acl *acl, accmode_t accmode, struct ucred *cred, int *privused)
59{
60	struct acl_entry *acl_other, *acl_mask;
61	accmode_t dac_granted;
62	accmode_t priv_granted;
63	accmode_t acl_mask_granted;
64	int group_matched, i;
65
66	KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND)) == 0,
67	    ("invalid bit in accmode"));
68	KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE),
69	    	("VAPPEND without VWRITE"));
70
71	/*
72	 * Look for a normal, non-privileged way to access the file/directory
73	 * as requested.  If it exists, go with that.  Otherwise, attempt to
74	 * use privileges granted via priv_granted.  In some cases, which
75	 * privileges to use may be ambiguous due to "best match", in which
76	 * case fall back on first match for the time being.
77	 */
78	if (privused != NULL)
79		*privused = 0;
80
81	/*
82	 * Determine privileges now, but don't apply until we've found a DAC
83	 * entry that matches but has failed to allow access.
84	 *
85	 * XXXRW: Ideally, we'd determine the privileges required before
86	 * asking for them.
87	 */
88	priv_granted = 0;
89
90	if (type == VDIR) {
91		if ((accmode & VEXEC) && !priv_check_cred(cred,
92		     PRIV_VFS_LOOKUP, 0))
93			priv_granted |= VEXEC;
94	} else {
95		/*
96		 * Ensure that at least one execute bit is on. Otherwise,
97		 * a privileged user will always succeed, and we don't want
98		 * this to happen unless the file really is executable.
99		 */
100		if ((accmode & VEXEC) && (acl_posix1e_acl_to_mode(acl) &
101		    (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 &&
102		    !priv_check_cred(cred, PRIV_VFS_EXEC, 0))
103			priv_granted |= VEXEC;
104	}
105
106	if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0))
107		priv_granted |= VREAD;
108
109	if (((accmode & VWRITE) || (accmode & VAPPEND)) &&
110	    !priv_check_cred(cred, PRIV_VFS_WRITE, 0))
111		priv_granted |= (VWRITE | VAPPEND);
112
113	if ((accmode & VADMIN) && !priv_check_cred(cred, PRIV_VFS_ADMIN, 0))
114		priv_granted |= VADMIN;
115
116	/*
117	 * The owner matches if the effective uid associated with the
118	 * credential matches that of the ACL_USER_OBJ entry.  While we're
119	 * doing the first scan, also cache the location of the ACL_MASK and
120	 * ACL_OTHER entries, preventing some future iterations.
121	 */
122	acl_mask = acl_other = NULL;
123	for (i = 0; i < acl->acl_cnt; i++) {
124		switch (acl->acl_entry[i].ae_tag) {
125		case ACL_USER_OBJ:
126			if (file_uid != cred->cr_uid)
127				break;
128			dac_granted = 0;
129			dac_granted |= VADMIN;
130			if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
131				dac_granted |= VEXEC;
132			if (acl->acl_entry[i].ae_perm & ACL_READ)
133				dac_granted |= VREAD;
134			if (acl->acl_entry[i].ae_perm & ACL_WRITE)
135				dac_granted |= (VWRITE | VAPPEND);
136			if ((accmode & dac_granted) == accmode)
137				return (0);
138
139			/*
140			 * XXXRW: Do privilege lookup here.
141			 */
142			if ((accmode & (dac_granted | priv_granted)) ==
143			    accmode) {
144				if (privused != NULL)
145					*privused = 1;
146				return (0);
147			}
148			goto error;
149
150		case ACL_MASK:
151			acl_mask = &acl->acl_entry[i];
152			break;
153
154		case ACL_OTHER:
155			acl_other = &acl->acl_entry[i];
156			break;
157
158		default:
159			break;
160		}
161	}
162
163	/*
164	 * An ACL_OTHER entry should always exist in a valid access ACL.  If
165	 * it doesn't, then generate a serious failure.  For now, this means
166	 * a debugging message and EPERM, but in the future should probably
167	 * be a panic.
168	 */
169	if (acl_other == NULL) {
170		/*
171		 * XXX This should never happen
172		 */
173		printf("vaccess_acl_posix1e: ACL_OTHER missing\n");
174		return (EPERM);
175	}
176
177	/*
178	 * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields are
179	 * masked by an ACL_MASK entry, if any.  As such, first identify the
180	 * ACL_MASK field, then iterate through identifying potential user
181	 * matches, then group matches.  If there is no ACL_MASK, assume that
182	 * the mask allows all requests to succeed.
183	 */
184	if (acl_mask != NULL) {
185		acl_mask_granted = 0;
186		if (acl_mask->ae_perm & ACL_EXECUTE)
187			acl_mask_granted |= VEXEC;
188		if (acl_mask->ae_perm & ACL_READ)
189			acl_mask_granted |= VREAD;
190		if (acl_mask->ae_perm & ACL_WRITE)
191			acl_mask_granted |= (VWRITE | VAPPEND);
192	} else
193		acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND;
194
195	/*
196	 * Check ACL_USER ACL entries.  There will either be one or no
197	 * matches; if there is one, we accept or rejected based on the
198	 * match; otherwise, we continue on to groups.
199	 */
200	for (i = 0; i < acl->acl_cnt; i++) {
201		switch (acl->acl_entry[i].ae_tag) {
202		case ACL_USER:
203			if (acl->acl_entry[i].ae_id != cred->cr_uid)
204				break;
205			dac_granted = 0;
206			if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
207				dac_granted |= VEXEC;
208			if (acl->acl_entry[i].ae_perm & ACL_READ)
209				dac_granted |= VREAD;
210			if (acl->acl_entry[i].ae_perm & ACL_WRITE)
211				dac_granted |= (VWRITE | VAPPEND);
212			dac_granted &= acl_mask_granted;
213			if ((accmode & dac_granted) == accmode)
214				return (0);
215			/*
216			 * XXXRW: Do privilege lookup here.
217			 */
218			if ((accmode & (dac_granted | priv_granted)) !=
219			    accmode)
220				goto error;
221
222			if (privused != NULL)
223				*privused = 1;
224			return (0);
225		}
226	}
227
228	/*
229	 * Group match is best-match, not first-match, so find a "best"
230	 * match.  Iterate across, testing each potential group match.  Make
231	 * sure we keep track of whether we found a match or not, so that we
232	 * know if we should try again with any available privilege, or if we
233	 * should move on to ACL_OTHER.
234	 */
235	group_matched = 0;
236	for (i = 0; i < acl->acl_cnt; i++) {
237		switch (acl->acl_entry[i].ae_tag) {
238		case ACL_GROUP_OBJ:
239			if (!groupmember(file_gid, cred))
240				break;
241			dac_granted = 0;
242			if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
243				dac_granted |= VEXEC;
244			if (acl->acl_entry[i].ae_perm & ACL_READ)
245				dac_granted |= VREAD;
246			if (acl->acl_entry[i].ae_perm & ACL_WRITE)
247				dac_granted |= (VWRITE | VAPPEND);
248			dac_granted  &= acl_mask_granted;
249
250			if ((accmode & dac_granted) == accmode)
251				return (0);
252
253			group_matched = 1;
254			break;
255
256		case ACL_GROUP:
257			if (!groupmember(acl->acl_entry[i].ae_id, cred))
258				break;
259			dac_granted = 0;
260			if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
261				dac_granted |= VEXEC;
262			if (acl->acl_entry[i].ae_perm & ACL_READ)
263				dac_granted |= VREAD;
264			if (acl->acl_entry[i].ae_perm & ACL_WRITE)
265				dac_granted |= (VWRITE | VAPPEND);
266			dac_granted  &= acl_mask_granted;
267
268			if ((accmode & dac_granted) == accmode)
269				return (0);
270
271			group_matched = 1;
272			break;
273
274		default:
275			break;
276		}
277	}
278
279	if (group_matched == 1) {
280		/*
281		 * There was a match, but it did not grant rights via pure
282		 * DAC.  Try again, this time with privilege.
283		 */
284		for (i = 0; i < acl->acl_cnt; i++) {
285			switch (acl->acl_entry[i].ae_tag) {
286			case ACL_GROUP_OBJ:
287				if (!groupmember(file_gid, cred))
288					break;
289				dac_granted = 0;
290				if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
291					dac_granted |= VEXEC;
292				if (acl->acl_entry[i].ae_perm & ACL_READ)
293					dac_granted |= VREAD;
294				if (acl->acl_entry[i].ae_perm & ACL_WRITE)
295					dac_granted |= (VWRITE | VAPPEND);
296				dac_granted &= acl_mask_granted;
297
298				/*
299				 * XXXRW: Do privilege lookup here.
300				 */
301				if ((accmode & (dac_granted | priv_granted))
302				    != accmode)
303					break;
304
305				if (privused != NULL)
306					*privused = 1;
307				return (0);
308
309			case ACL_GROUP:
310				if (!groupmember(acl->acl_entry[i].ae_id,
311				    cred))
312					break;
313				dac_granted = 0;
314				if (acl->acl_entry[i].ae_perm & ACL_EXECUTE)
315				dac_granted |= VEXEC;
316				if (acl->acl_entry[i].ae_perm & ACL_READ)
317					dac_granted |= VREAD;
318				if (acl->acl_entry[i].ae_perm & ACL_WRITE)
319					dac_granted |= (VWRITE | VAPPEND);
320				dac_granted &= acl_mask_granted;
321
322				/*
323				 * XXXRW: Do privilege lookup here.
324				 */
325				if ((accmode & (dac_granted | priv_granted))
326				    != accmode)
327					break;
328
329				if (privused != NULL)
330					*privused = 1;
331				return (0);
332
333			default:
334				break;
335			}
336		}
337		/*
338		 * Even with privilege, group membership was not sufficient.
339		 * Return failure.
340		 */
341		goto error;
342	}
343
344	/*
345	 * Fall back on ACL_OTHER.  ACL_MASK is not applied to ACL_OTHER.
346	 */
347	dac_granted = 0;
348	if (acl_other->ae_perm & ACL_EXECUTE)
349		dac_granted |= VEXEC;
350	if (acl_other->ae_perm & ACL_READ)
351		dac_granted |= VREAD;
352	if (acl_other->ae_perm & ACL_WRITE)
353		dac_granted |= (VWRITE | VAPPEND);
354
355	if ((accmode & dac_granted) == accmode)
356		return (0);
357	/*
358	 * XXXRW: Do privilege lookup here.
359	 */
360	if ((accmode & (dac_granted | priv_granted)) == accmode) {
361		if (privused != NULL)
362			*privused = 1;
363		return (0);
364	}
365
366error:
367	return ((accmode & VADMIN) ? EPERM : EACCES);
368}
369
370/*
371 * For the purposes of filesystems maintaining the _OBJ entries in an inode
372 * with a mode_t field, this routine converts a mode_t entry to an
373 * acl_perm_t.
374 */
375acl_perm_t
376acl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode)
377{
378	acl_perm_t	perm = 0;
379
380	switch(tag) {
381	case ACL_USER_OBJ:
382		if (mode & S_IXUSR)
383			perm |= ACL_EXECUTE;
384		if (mode & S_IRUSR)
385			perm |= ACL_READ;
386		if (mode & S_IWUSR)
387			perm |= ACL_WRITE;
388		return (perm);
389
390	case ACL_GROUP_OBJ:
391		if (mode & S_IXGRP)
392			perm |= ACL_EXECUTE;
393		if (mode & S_IRGRP)
394			perm |= ACL_READ;
395		if (mode & S_IWGRP)
396			perm |= ACL_WRITE;
397		return (perm);
398
399	case ACL_OTHER:
400		if (mode & S_IXOTH)
401			perm |= ACL_EXECUTE;
402		if (mode & S_IROTH)
403			perm |= ACL_READ;
404		if (mode & S_IWOTH)
405			perm |= ACL_WRITE;
406		return (perm);
407
408	default:
409		printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag);
410		return (0);
411	}
412}
413
414/*
415 * Given inode information (uid, gid, mode), return an acl entry of the
416 * appropriate type.
417 */
418struct acl_entry
419acl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode)
420{
421	struct acl_entry	acl_entry;
422
423	acl_entry.ae_tag = tag;
424	acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode);
425	acl_entry.ae_entry_type = 0;
426	acl_entry.ae_flags = 0;
427	switch(tag) {
428	case ACL_USER_OBJ:
429		acl_entry.ae_id = uid;
430		break;
431
432	case ACL_GROUP_OBJ:
433		acl_entry.ae_id = gid;
434		break;
435
436	case ACL_OTHER:
437		acl_entry.ae_id = ACL_UNDEFINED_ID;
438		break;
439
440	default:
441		acl_entry.ae_id = ACL_UNDEFINED_ID;
442		printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag);
443	}
444
445	return (acl_entry);
446}
447
448/*
449 * Utility function to generate a file mode given appropriate ACL entries.
450 */
451mode_t
452acl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry,
453    struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry)
454{
455	mode_t	mode;
456
457	mode = 0;
458	if (acl_user_obj_entry->ae_perm & ACL_EXECUTE)
459		mode |= S_IXUSR;
460	if (acl_user_obj_entry->ae_perm & ACL_READ)
461		mode |= S_IRUSR;
462	if (acl_user_obj_entry->ae_perm & ACL_WRITE)
463		mode |= S_IWUSR;
464	if (acl_group_obj_entry->ae_perm & ACL_EXECUTE)
465		mode |= S_IXGRP;
466	if (acl_group_obj_entry->ae_perm & ACL_READ)
467		mode |= S_IRGRP;
468	if (acl_group_obj_entry->ae_perm & ACL_WRITE)
469		mode |= S_IWGRP;
470	if (acl_other_entry->ae_perm & ACL_EXECUTE)
471		mode |= S_IXOTH;
472	if (acl_other_entry->ae_perm & ACL_READ)
473		mode |= S_IROTH;
474	if (acl_other_entry->ae_perm & ACL_WRITE)
475		mode |= S_IWOTH;
476
477	return (mode);
478}
479
480/*
481 * Utility function to generate a file mode given a complete POSIX.1e access
482 * ACL.  Note that if the ACL is improperly formed, this may result in a
483 * panic.
484 */
485mode_t
486acl_posix1e_acl_to_mode(struct acl *acl)
487{
488	struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other;
489	int i;
490
491	/*
492	 * Find the ACL entries relevant to a POSIX permission mode.
493	 */
494	acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL;
495	for (i = 0; i < acl->acl_cnt; i++) {
496		switch (acl->acl_entry[i].ae_tag) {
497		case ACL_USER_OBJ:
498			acl_user_obj = &acl->acl_entry[i];
499			break;
500
501		case ACL_GROUP_OBJ:
502			acl_group_obj = &acl->acl_entry[i];
503			break;
504
505		case ACL_OTHER:
506			acl_other = &acl->acl_entry[i];
507			break;
508
509		case ACL_MASK:
510			acl_mask = &acl->acl_entry[i];
511			break;
512
513		case ACL_USER:
514		case ACL_GROUP:
515			break;
516
517		default:
518			panic("acl_posix1e_acl_to_mode: bad ae_tag");
519		}
520	}
521
522	if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL)
523		panic("acl_posix1e_acl_to_mode: missing base ae_tags");
524
525	/*
526	 * POSIX.1e specifies that if there is an ACL_MASK entry, we replace
527	 * the mode "group" bits with its permissions.  If there isn't, we
528	 * use the ACL_GROUP_OBJ permissions.
529	 */
530	if (acl_mask != NULL)
531		return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask,
532		    acl_other));
533	else
534		return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj,
535		    acl_other));
536}
537
538/*
539 * Perform a syntactic check of the ACL, sufficient to allow an implementing
540 * filesystem to determine if it should accept this and rely on the POSIX.1e
541 * ACL properties.
542 */
543int
544acl_posix1e_check(struct acl *acl)
545{
546	int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group;
547	int num_acl_mask, num_acl_other, i;
548
549	/*
550	 * Verify that the number of entries does not exceed the maximum
551	 * defined for acl_t.
552	 *
553	 * Verify that the correct number of various sorts of ae_tags are
554	 * present:
555	 *   Exactly one ACL_USER_OBJ
556	 *   Exactly one ACL_GROUP_OBJ
557	 *   Exactly one ACL_OTHER
558	 *   If any ACL_USER or ACL_GROUP entries appear, then exactly one
559	 *   ACL_MASK entry must also appear.
560	 *
561	 * Verify that all ae_perm entries are in ACL_PERM_BITS.
562	 *
563	 * Verify all ae_tag entries are understood by this implementation.
564	 *
565	 * Note: Does not check for uniqueness of qualifier (ae_id) field.
566	 */
567	num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group =
568	    num_acl_mask = num_acl_other = 0;
569	if (acl->acl_cnt > ACL_MAX_ENTRIES)
570		return (EINVAL);
571	for (i = 0; i < acl->acl_cnt; i++) {
572		/*
573		 * Check for a valid tag.
574		 */
575		switch(acl->acl_entry[i].ae_tag) {
576		case ACL_USER_OBJ:
577			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */
578			if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID)
579				return (EINVAL);
580			num_acl_user_obj++;
581			break;
582		case ACL_GROUP_OBJ:
583			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */
584			if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID)
585				return (EINVAL);
586			num_acl_group_obj++;
587			break;
588		case ACL_USER:
589			if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID)
590				return (EINVAL);
591			num_acl_user++;
592			break;
593		case ACL_GROUP:
594			if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID)
595				return (EINVAL);
596			num_acl_group++;
597			break;
598		case ACL_OTHER:
599			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */
600			if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID)
601				return (EINVAL);
602			num_acl_other++;
603			break;
604		case ACL_MASK:
605			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */
606			if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID)
607				return (EINVAL);
608			num_acl_mask++;
609			break;
610		default:
611			return (EINVAL);
612		}
613		/*
614		 * Check for valid perm entries.
615		 */
616		if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) !=
617		    ACL_PERM_BITS)
618			return (EINVAL);
619	}
620	if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) ||
621	    (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1))
622		return (EINVAL);
623	if (((num_acl_group != 0) || (num_acl_user != 0)) &&
624	    (num_acl_mask != 1))
625		return (EINVAL);
626	return (0);
627}
628
629/*
630 * Given a requested mode for a new object, and a default ACL, combine the
631 * two to produce a new mode.  Be careful not to clear any bits that aren't
632 * intended to be affected by the POSIX.1e ACL.  Eventually, this might also
633 * take the cmask as an argument, if we push that down into
634 * per-filesystem-code.
635 */
636mode_t
637acl_posix1e_newfilemode(mode_t cmode, struct acl *dacl)
638{
639	mode_t mode;
640
641	mode = cmode;
642	/*
643	 * The current composition policy is that a permission bit must be
644	 * set in *both* the ACL and the requested creation mode for it to
645	 * appear in the resulting mode/ACL.  First clear any possibly
646	 * effected bits, then reconstruct.
647	 */
648	mode &= ACL_PRESERVE_MASK;
649	mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl));
650
651	return (mode);
652}
653
654
655static int
656acl_posix1e_modload(module_t mod, int what, void *arg)
657{
658	int ret;
659
660	ret = 0;
661
662	switch (what) {
663	case MOD_LOAD:
664	case MOD_SHUTDOWN:
665		break;
666
667	case MOD_QUIESCE:
668		/* XXX TODO */
669		ret = 0;
670		break;
671
672	case MOD_UNLOAD:
673		/* XXX TODO */
674		ret = 0;
675		break;
676	default:
677		ret = EINVAL;
678		break;
679	}
680
681	return (ret);
682}
683
684static moduledata_t acl_posix1e_mod = {
685	"acl_posix1e",
686	acl_posix1e_modload,
687	NULL
688};
689
690DECLARE_MODULE(acl_posix1e, acl_posix1e_mod, SI_SUB_VFS, SI_ORDER_FIRST);
691MODULE_VERSION(acl_posix1e, 1);
692