Implement NFSv4 ACL support for UFS.
Reviewed by: rwatson
This commit is contained in:
parent
fdf85ee3e5
commit
f04a989f2d
@ -251,8 +251,11 @@ dumpfs(const char *name)
|
||||
printf("gjournal ");
|
||||
if (fsflags & FS_FLAGS_UPDATED)
|
||||
printf("fs_flags expanded ");
|
||||
if (fsflags & FS_NFS4ACLS)
|
||||
printf("nfsv4acls ");
|
||||
fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_INDEXDIRS |
|
||||
FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED);
|
||||
FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED |
|
||||
FS_NFS4ACLS);
|
||||
if (fsflags != 0)
|
||||
printf("unknown flags (%#x)", fsflags);
|
||||
putchar('\n');
|
||||
|
@ -54,6 +54,7 @@ struct mntopt {
|
||||
#define MOPT_SNAPSHOT { "snapshot", 0, MNT_SNAPSHOT, 0 }
|
||||
#define MOPT_MULTILABEL { "multilabel", 0, MNT_MULTILABEL, 0 }
|
||||
#define MOPT_ACLS { "acls", 0, MNT_ACLS, 0 }
|
||||
#define MOPT_NFS4ACLS { "nfsv4acls", 0, MNT_NFS4ACLS, 0 }
|
||||
|
||||
/* Control flags. */
|
||||
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
|
||||
@ -87,7 +88,8 @@ struct mntopt {
|
||||
MOPT_NOCLUSTERR, \
|
||||
MOPT_NOCLUSTERW, \
|
||||
MOPT_MULTILABEL, \
|
||||
MOPT_ACLS
|
||||
MOPT_ACLS, \
|
||||
MOPT_NFS4ACLS
|
||||
|
||||
void getmntopts(const char *, const struct mntopt *, int *, int *);
|
||||
void rmslashes(char *, char *);
|
||||
|
@ -120,11 +120,14 @@ takes effect.
|
||||
The following options are available:
|
||||
.Bl -tag -width indent
|
||||
.It Cm acls
|
||||
Enable Access Control Lists, or ACLS, which can be customized via the
|
||||
Enable POSIX.1e Access Control Lists, or ACLs, which can be customized via the
|
||||
.Xr setfacl 1
|
||||
and
|
||||
.Xr getfacl 1
|
||||
commands.
|
||||
This flag is mutually exclusive with
|
||||
.Cm nfsv4acls
|
||||
flag.
|
||||
.It Cm async
|
||||
All
|
||||
.Tn I/O
|
||||
@ -186,6 +189,15 @@ See
|
||||
.Xr mac 4
|
||||
for more information, which cause the multilabel mount flag to be set
|
||||
automatically at mount-time.
|
||||
.It Cm nfsv4acls
|
||||
Enable NFSv4 ACLs, which can be customized via the
|
||||
.Xr setfacl 1
|
||||
and
|
||||
.Xr getfacl 1
|
||||
commands.
|
||||
This flag is mutually exclusive with
|
||||
.Cm acls
|
||||
flag.
|
||||
.It Cm noasync
|
||||
Metadata I/O should be done synchronously, while data I/O should be done
|
||||
asynchronously.
|
||||
|
@ -111,6 +111,7 @@ static struct opt {
|
||||
{ MNT_SOFTDEP, "soft-updates" },
|
||||
{ MNT_MULTILABEL, "multilabel" },
|
||||
{ MNT_ACLS, "acls" },
|
||||
{ MNT_NFS4ACLS, "nfsv4acls" },
|
||||
{ MNT_GJOURNAL, "gjournal" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
@ -918,6 +919,7 @@ flags2opts(int flags)
|
||||
if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir");
|
||||
if (flags & MNT_MULTILABEL) res = catopt(res, "multilabel");
|
||||
if (flags & MNT_ACLS) res = catopt(res, "acls");
|
||||
if (flags & MNT_NFS4ACLS) res = catopt(res, "nfsv4acls");
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
@ -44,6 +44,7 @@
|
||||
.Op Fl L Ar volname
|
||||
.Op Fl l Cm enable | disable
|
||||
.Op Fl m Ar minfree
|
||||
.Op Fl N Cm enable | disable
|
||||
.Op Fl n Cm enable | disable
|
||||
.Op Fl o Cm space | time
|
||||
.Op Fl p
|
||||
@ -70,7 +71,7 @@ this option will cause all backups to be modified as well as the
|
||||
primary super-block.
|
||||
This is potentially dangerous - use with caution.
|
||||
.It Fl a Cm enable | disable
|
||||
Turn on/off the administrative ACL enable flag.
|
||||
Turn on/off the administrative POSIX.1e ACL enable flag.
|
||||
.It Fl e Ar maxbpg
|
||||
Indicate the maximum number of blocks any single file can
|
||||
allocate out of a cylinder group before it is forced to begin
|
||||
@ -114,6 +115,8 @@ factor of three over the performance obtained at a 10% threshold.
|
||||
If the value is raised above the current usage level,
|
||||
users will be unable to allocate files until enough files have
|
||||
been deleted to get under the higher threshold.
|
||||
.It Fl N Cm enable | disable
|
||||
Turn on/off the administrative NFSv4 ACL enable flag.
|
||||
.It Fl n Cm enable | disable
|
||||
Turn on/off soft updates.
|
||||
.It Fl o Cm space | time
|
||||
|
@ -76,12 +76,12 @@ void printfs(void);
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *avalue, *Jvalue, *Lvalue, *lvalue, *nvalue;
|
||||
char *avalue, *Jvalue, *Lvalue, *lvalue, *Nvalue, *nvalue;
|
||||
const char *special, *on;
|
||||
const char *name;
|
||||
int active;
|
||||
int Aflag, aflag, eflag, evalue, fflag, fvalue, Jflag, Lflag, lflag;
|
||||
int mflag, mvalue, nflag, oflag, ovalue, pflag, sflag, svalue;
|
||||
int mflag, mvalue, Nflag, nflag, oflag, ovalue, pflag, sflag, svalue;
|
||||
int ch, found_arg, i;
|
||||
const char *chg[2];
|
||||
struct ufs_args args;
|
||||
@ -90,12 +90,12 @@ main(int argc, char *argv[])
|
||||
if (argc < 3)
|
||||
usage();
|
||||
Aflag = aflag = eflag = fflag = Jflag = Lflag = lflag = mflag = 0;
|
||||
nflag = oflag = pflag = sflag = 0;
|
||||
avalue = Jvalue = Lvalue = lvalue = nvalue = NULL;
|
||||
Nflag = nflag = oflag = pflag = sflag = 0;
|
||||
avalue = Jvalue = Lvalue = lvalue = Nvalue = nvalue = NULL;
|
||||
evalue = fvalue = mvalue = ovalue = svalue = 0;
|
||||
active = 0;
|
||||
found_arg = 0; /* At least one arg is required. */
|
||||
while ((ch = getopt(argc, argv, "Aa:e:f:J:L:l:m:n:o:ps:")) != -1)
|
||||
while ((ch = getopt(argc, argv, "Aa:e:f:J:L:l:m:N:n:o:ps:")) != -1)
|
||||
switch (ch) {
|
||||
|
||||
case 'A':
|
||||
@ -105,7 +105,7 @@ main(int argc, char *argv[])
|
||||
|
||||
case 'a':
|
||||
found_arg = 1;
|
||||
name = "ACLs";
|
||||
name = "POSIX.1e ACLs";
|
||||
avalue = optarg;
|
||||
if (strcmp(avalue, "enable") &&
|
||||
strcmp(avalue, "disable")) {
|
||||
@ -187,6 +187,18 @@ main(int argc, char *argv[])
|
||||
mflag = 1;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
found_arg = 1;
|
||||
name = "NFSv4 ACLs";
|
||||
Nvalue = optarg;
|
||||
if (strcmp(Nvalue, "enable") &&
|
||||
strcmp(Nvalue, "disable")) {
|
||||
errx(10, "bad %s (options are %s)",
|
||||
name, "`enable' or `disable'");
|
||||
}
|
||||
Nflag = 1;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
found_arg = 1;
|
||||
name = "soft updates";
|
||||
@ -255,10 +267,13 @@ main(int argc, char *argv[])
|
||||
strlcpy(sblock.fs_volname, Lvalue, MAXVOLLEN);
|
||||
}
|
||||
if (aflag) {
|
||||
name = "ACLs";
|
||||
name = "POSIX.1e ACLs";
|
||||
if (strcmp(avalue, "enable") == 0) {
|
||||
if (sblock.fs_flags & FS_ACLS) {
|
||||
warnx("%s remains unchanged as enabled", name);
|
||||
} else if (sblock.fs_flags & FS_NFS4ACLS) {
|
||||
warnx("%s and NFSv4 ACLs are mutually "
|
||||
"exclusive", name);
|
||||
} else {
|
||||
sblock.fs_flags |= FS_ACLS;
|
||||
warnx("%s set", name);
|
||||
@ -349,6 +364,29 @@ main(int argc, char *argv[])
|
||||
warnx(OPTWARN, "space", "<", MINFREE);
|
||||
}
|
||||
}
|
||||
if (Nflag) {
|
||||
name = "NFSv4 ACLs";
|
||||
if (strcmp(Nvalue, "enable") == 0) {
|
||||
if (sblock.fs_flags & FS_NFS4ACLS) {
|
||||
warnx("%s remains unchanged as enabled", name);
|
||||
} else if (sblock.fs_flags & FS_ACLS) {
|
||||
warnx("%s and POSIX.1e ACLs are mutually "
|
||||
"exclusive", name);
|
||||
} else {
|
||||
sblock.fs_flags |= FS_NFS4ACLS;
|
||||
warnx("%s set", name);
|
||||
}
|
||||
} else if (strcmp(Nvalue, "disable") == 0) {
|
||||
if ((~sblock.fs_flags & FS_NFS4ACLS) ==
|
||||
FS_NFS4ACLS) {
|
||||
warnx("%s remains unchanged as disabled",
|
||||
name);
|
||||
} else {
|
||||
sblock.fs_flags &= ~FS_NFS4ACLS;
|
||||
warnx("%s cleared", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nflag) {
|
||||
name = "soft updates";
|
||||
if (strcmp(nvalue, "enable") == 0) {
|
||||
@ -423,16 +461,18 @@ usage(void)
|
||||
fprintf(stderr, "%s\n%s\n%s\n%s\n",
|
||||
"usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]",
|
||||
" [-J enable | disable ] [-L volname] [-l enable | disable]",
|
||||
" [-m minfree] [-n enable | disable] [-o space | time] [-p]",
|
||||
" [-s avgfpdir] special | filesystem");
|
||||
" [-m minfree] [-N enable | disable] [-n enable | disable]",
|
||||
" [-o space | time] [-p] [-s avgfpdir] special | filesystem");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
void
|
||||
printfs(void)
|
||||
{
|
||||
warnx("ACLs: (-a) %s",
|
||||
warnx("POSIX.1e ACLs: (-a) %s",
|
||||
(sblock.fs_flags & FS_ACLS)? "enabled" : "disabled");
|
||||
warnx("NFSv4 ACLs: (-N) %s",
|
||||
(sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled");
|
||||
warnx("MAC multilabel: (-l) %s",
|
||||
(sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled");
|
||||
warnx("soft updates: (-n) %s",
|
||||
|
@ -239,6 +239,7 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
|
||||
#define MNT_NOATIME 0x10000000 /* disable update of file access time */
|
||||
#define MNT_NOCLUSTERR 0x40000000 /* disable cluster read */
|
||||
#define MNT_NOCLUSTERW 0x80000000 /* disable cluster write */
|
||||
#define MNT_NFS4ACLS 0x00000010
|
||||
|
||||
/*
|
||||
* NFS export related mount flags.
|
||||
@ -274,7 +275,7 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
|
||||
MNT_ROOTFS | MNT_NOATIME | MNT_NOCLUSTERR| \
|
||||
MNT_NOCLUSTERW | MNT_SUIDDIR | MNT_SOFTDEP | \
|
||||
MNT_IGNORE | MNT_EXPUBLIC | MNT_NOSYMFOLLOW | \
|
||||
MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS)
|
||||
MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS | MNT_NFS4ACLS)
|
||||
|
||||
/* Mask of flags that can be updated. */
|
||||
#define MNT_UPDATEMASK (MNT_NOSUID | MNT_NOEXEC | \
|
||||
@ -282,7 +283,7 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
|
||||
MNT_NOATIME | \
|
||||
MNT_NOSYMFOLLOW | MNT_IGNORE | \
|
||||
MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR | \
|
||||
MNT_ACLS | MNT_USER)
|
||||
MNT_ACLS | MNT_USER | MNT_NFS4ACLS)
|
||||
|
||||
/*
|
||||
* External filesystem command modifier flags.
|
||||
@ -299,10 +300,6 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
|
||||
#define MNT_BYFSID 0x08000000 /* specify filesystem by ID. */
|
||||
#define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \
|
||||
MNT_FORCE | MNT_SNAPSHOT | MNT_BYFSID)
|
||||
/*
|
||||
* Still available.
|
||||
*/
|
||||
#define MNT_SPARE_0x00000010 0x00000010
|
||||
/*
|
||||
* Internal filesystem control flags stored in mnt_kern_flag.
|
||||
*
|
||||
|
@ -128,7 +128,7 @@ static struct buf_ops ffs_ops = {
|
||||
static const char *ffs_opts[] = { "acls", "async", "noatime", "noclusterr",
|
||||
"noclusterw", "noexec", "export", "force", "from", "multilabel",
|
||||
"snapshot", "nosuid", "suiddir", "nosymfollow", "sync",
|
||||
"union", NULL };
|
||||
"union", "nfsv4acls", NULL };
|
||||
|
||||
static int
|
||||
ffs_mount(struct mount *mp)
|
||||
@ -177,6 +177,15 @@ ffs_mount(struct mount *mp)
|
||||
vfs_deleteopt(mp->mnt_opt, "snapshot");
|
||||
}
|
||||
|
||||
if (vfs_getopt(mp->mnt_optnew, "nfsv4acls", NULL, NULL) == 0) {
|
||||
if (mntorflags & MNT_ACLS) {
|
||||
printf("WARNING: \"acls\" and \"nfsv4acls\" "
|
||||
"options are mutually exclusive\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
mntorflags |= MNT_NFS4ACLS;
|
||||
}
|
||||
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag = (mp->mnt_flag | mntorflags) & ~mntandnotflags;
|
||||
MNT_IUNLOCK(mp);
|
||||
@ -360,6 +369,13 @@ ffs_mount(struct mount *mp)
|
||||
MNT_IUNLOCK(mp);
|
||||
}
|
||||
|
||||
if ((fs->fs_flags & FS_NFS4ACLS) != 0) {
|
||||
/* XXX: Set too late ? */
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag |= MNT_NFS4ACLS;
|
||||
MNT_IUNLOCK(mp);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a snapshot request, take the snapshot.
|
||||
*/
|
||||
@ -834,7 +850,13 @@ ffs_mountfs(devvp, mp, td)
|
||||
if ((fs->fs_flags & FS_ACLS) != 0) {
|
||||
#ifdef UFS_ACL
|
||||
MNT_ILOCK(mp);
|
||||
|
||||
if (mp->mnt_flag & MNT_NFS4ACLS)
|
||||
printf("WARNING: ACLs flag on fs conflicts with "
|
||||
"\"nfsv4acls\" mount option; option ignored\n");
|
||||
mp->mnt_flag &= ~MNT_NFS4ACLS;
|
||||
mp->mnt_flag |= MNT_ACLS;
|
||||
|
||||
MNT_IUNLOCK(mp);
|
||||
#else
|
||||
printf(
|
||||
@ -842,6 +864,24 @@ ffs_mountfs(devvp, mp, td)
|
||||
mp->mnt_stat.f_mntonname);
|
||||
#endif
|
||||
}
|
||||
if ((fs->fs_flags & FS_NFS4ACLS) != 0) {
|
||||
#ifdef UFS_ACL
|
||||
MNT_ILOCK(mp);
|
||||
|
||||
if (mp->mnt_flag & MNT_ACLS)
|
||||
printf("WARNING: NFSv4 ACLs flag on fs conflicts with "
|
||||
"\"acls\" mount option; option ignored\n");
|
||||
mp->mnt_flag &= ~MNT_ACLS;
|
||||
mp->mnt_flag |= MNT_NFS4ACLS;
|
||||
|
||||
MNT_IUNLOCK(mp);
|
||||
#else
|
||||
printf(
|
||||
"WARNING: %s: NFSv4 ACLs flag on fs but no ACLs support\n",
|
||||
mp->mnt_stat.f_mntonname);
|
||||
#endif
|
||||
}
|
||||
|
||||
ump->um_mountp = mp;
|
||||
ump->um_dev = dev;
|
||||
ump->um_devvp = devvp;
|
||||
|
@ -393,22 +393,24 @@ CTASSERT(sizeof(struct fs) == 1376);
|
||||
* flag to indicate that the indicies need to be rebuilt (by fsck) before
|
||||
* they can be used.
|
||||
*
|
||||
* FS_ACLS indicates that ACLs are administratively enabled for the
|
||||
* file system, so they should be loaded from extended attributes,
|
||||
* FS_ACLS indicates that POSIX.1e ACLs are administratively enabled
|
||||
* for the file system, so they should be loaded from extended attributes,
|
||||
* observed for access control purposes, and be administered by object
|
||||
* owners. FS_MULTILABEL indicates that the TrustedBSD MAC Framework
|
||||
* should attempt to back MAC labels into extended attributes on the
|
||||
* file system rather than maintain a single mount label for all
|
||||
* objects.
|
||||
* owners. FS_NFS4ACLS indicates that NFSv4 ACLs are administratively
|
||||
* enabled. This flag is mutually exclusive with FS_ACLS. FS_MULTILABEL
|
||||
* indicates that the TrustedBSD MAC Framework should attempt to back MAC
|
||||
* labels into extended attributes on the file system rather than maintain
|
||||
* a single mount label for all objects.
|
||||
*/
|
||||
#define FS_UNCLEAN 0x01 /* filesystem not clean at mount */
|
||||
#define FS_DOSOFTDEP 0x02 /* filesystem using soft dependencies */
|
||||
#define FS_NEEDSFSCK 0x04 /* filesystem needs sync fsck before mount */
|
||||
#define FS_INDEXDIRS 0x08 /* kernel supports indexed directories */
|
||||
#define FS_ACLS 0x10 /* file system has ACLs enabled */
|
||||
#define FS_MULTILABEL 0x20 /* file system is MAC multi-label */
|
||||
#define FS_GJOURNAL 0x40 /* gjournaled file system */
|
||||
#define FS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */
|
||||
#define FS_UNCLEAN 0x0001 /* filesystem not clean at mount */
|
||||
#define FS_DOSOFTDEP 0x0002 /* filesystem using soft dependencies */
|
||||
#define FS_NEEDSFSCK 0x0004 /* filesystem needs sync fsck before mount */
|
||||
#define FS_INDEXDIRS 0x0008 /* kernel supports indexed directories */
|
||||
#define FS_ACLS 0x0010 /* file system has POSIX.1e ACLs enabled */
|
||||
#define FS_MULTILABEL 0x0020 /* file system is MAC multi-label */
|
||||
#define FS_GJOURNAL 0x0040 /* gjournaled file system */
|
||||
#define FS_FLAGS_UPDATED 0x0080 /* flags have been moved to new location */
|
||||
#define FS_NFS4ACLS 0x0100 /* file system has NFSv4 ACLs enabled */
|
||||
|
||||
/*
|
||||
* Macros to access bits in the fs_active array.
|
||||
|
@ -37,6 +37,8 @@
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
int ufs_getacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td);
|
||||
int ufs_setacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td);
|
||||
void ufs_sync_acl_from_inode(struct inode *ip, struct acl *acl);
|
||||
void ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip);
|
||||
|
||||
|
@ -140,6 +140,81 @@ ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip)
|
||||
DIP_SET(ip, i_mode, ip->i_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve NFSv4 ACL, skipping access checks. Must be used in UFS code
|
||||
* instead of VOP_GETACL() when we don't want to be restricted by the user
|
||||
* not having ACL_READ_ACL permission, e.g. when calculating inherited ACL
|
||||
* or in ufs_vnops.c:ufs_accessx().
|
||||
*/
|
||||
int
|
||||
ufs_getacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td)
|
||||
{
|
||||
int error, len;
|
||||
struct inode *ip = VTOI(vp);
|
||||
|
||||
len = sizeof(*aclp);
|
||||
bzero(aclp, len);
|
||||
|
||||
error = vn_extattr_get(vp, IO_NODELOCKED,
|
||||
NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME,
|
||||
&len, (char *) aclp, td);
|
||||
aclp->acl_maxcnt = ACL_MAX_ENTRIES;
|
||||
if (error == ENOATTR) {
|
||||
/*
|
||||
* Legitimately no ACL set on object, purely
|
||||
* emulate it through the inode.
|
||||
*/
|
||||
acl_nfs4_sync_acl_from_mode(aclp, ip->i_mode, ip->i_uid);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (len != sizeof(*aclp)) {
|
||||
/*
|
||||
* A short (or long) read, meaning that for
|
||||
* some reason the ACL is corrupted. Return
|
||||
* EPERM since the object DAC protections
|
||||
* are unsafe.
|
||||
*/
|
||||
printf("ufs_getacl_nfs4(): Loaded invalid ACL ("
|
||||
"%d bytes), inumber %d on %s\n", len,
|
||||
ip->i_number, ip->i_fs->fs_fsmnt);
|
||||
|
||||
return (EPERM);
|
||||
}
|
||||
|
||||
error = acl_nfs4_check(aclp, vp->v_type == VDIR);
|
||||
if (error) {
|
||||
printf("ufs_getacl_nfs4(): Loaded invalid ACL "
|
||||
"(failed acl_nfs4_check), inumber %d on %s\n",
|
||||
ip->i_number, ip->i_fs->fs_fsmnt);
|
||||
|
||||
return (EPERM);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_getacl_nfs4(struct vop_getacl_args *ap)
|
||||
{
|
||||
int error;
|
||||
|
||||
if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
error = VOP_ACCESSX(ap->a_vp, VREAD_ACL, ap->a_td->td_ucred, ap->a_td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
error = ufs_getacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read POSIX.1e ACL from an EA. Return error if its not found
|
||||
* or if any other error has occured.
|
||||
@ -209,7 +284,7 @@ ufs_getacl_posix1e(struct vop_getacl_args *ap)
|
||||
* ACLs, remove this check.
|
||||
*/
|
||||
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
|
||||
return (EOPNOTSUPP);
|
||||
return (EINVAL);
|
||||
|
||||
old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO);
|
||||
|
||||
@ -282,9 +357,117 @@ ufs_getacl(ap)
|
||||
} */ *ap;
|
||||
{
|
||||
|
||||
if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
if (ap->a_type == ACL_TYPE_NFS4)
|
||||
return (ufs_getacl_nfs4(ap));
|
||||
|
||||
return (ufs_getacl_posix1e(ap));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set NFSv4 ACL without doing any access checking. This is required
|
||||
* e.g. by the UFS code that implements ACL inheritance, or from
|
||||
* ufs_vnops.c:ufs_chmod(), as some of the checks have to be skipped
|
||||
* in that case, and others are redundant.
|
||||
*/
|
||||
int
|
||||
ufs_setacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td)
|
||||
{
|
||||
int error;
|
||||
mode_t mode;
|
||||
struct inode *ip = VTOI(vp);
|
||||
|
||||
KASSERT(acl_nfs4_check(aclp, vp->v_type == VDIR) == 0,
|
||||
("invalid ACL passed to ufs_setacl_nfs4_internal"));
|
||||
|
||||
if (acl_nfs4_is_trivial(aclp, ip->i_uid)) {
|
||||
error = vn_extattr_rm(vp, IO_NODELOCKED,
|
||||
NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME, td);
|
||||
|
||||
/*
|
||||
* An attempt to remove ACL from a file that didn't have
|
||||
* any extended entries is not an error.
|
||||
*/
|
||||
if (error == ENOATTR)
|
||||
error = 0;
|
||||
|
||||
} else {
|
||||
error = vn_extattr_set(vp, IO_NODELOCKED,
|
||||
NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME,
|
||||
sizeof(*aclp), (char *) aclp, td);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map lack of attribute definition in UFS_EXTATTR into lack of
|
||||
* support for ACLs on the filesystem.
|
||||
*/
|
||||
if (error == ENOATTR)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
mode = ip->i_mode;
|
||||
|
||||
acl_nfs4_sync_mode_from_acl(&mode, aclp);
|
||||
|
||||
ip->i_mode &= ACL_PRESERVE_MASK;
|
||||
ip->i_mode |= mode;
|
||||
DIP_SET(ip, i_mode, ip->i_mode);
|
||||
ip->i_flag |= IN_CHANGE;
|
||||
|
||||
VN_KNOTE_UNLOCKED(vp, NOTE_ATTRIB);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_setacl_nfs4(struct vop_setacl_args *ap)
|
||||
{
|
||||
int error;
|
||||
struct inode *ip = VTOI(ap->a_vp);
|
||||
|
||||
if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
|
||||
return (EROFS);
|
||||
|
||||
if (ap->a_aclp == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, ap->a_cred,
|
||||
ap->a_td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Authorize the ACL operation.
|
||||
*/
|
||||
if (ip->i_flags & (IMMUTABLE | APPEND))
|
||||
return (EPERM);
|
||||
|
||||
/*
|
||||
* Must hold VWRITE_ACL or have appropriate privilege.
|
||||
*/
|
||||
if ((error = VOP_ACCESSX(ap->a_vp, VWRITE_ACL, ap->a_cred, ap->a_td)))
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* With NFSv4 ACLs, chmod(2) may need to add additional entries.
|
||||
* Make sure it has enough room for that - splitting every entry
|
||||
* into two and appending "canonical six" entries at the end.
|
||||
*/
|
||||
if (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
|
||||
return (ENOSPC);
|
||||
|
||||
error = ufs_setacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the ACL on a file.
|
||||
*
|
||||
@ -302,7 +485,7 @@ ufs_setacl_posix1e(struct vop_setacl_args *ap)
|
||||
struct oldacl *old;
|
||||
|
||||
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
|
||||
return (EOPNOTSUPP);
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* If this is a set operation rather than a delete operation,
|
||||
@ -422,16 +605,43 @@ ufs_setacl(ap)
|
||||
struct thread *td;
|
||||
} */ *ap;
|
||||
{
|
||||
if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
if (ap->a_type == ACL_TYPE_NFS4)
|
||||
return (ufs_setacl_nfs4(ap));
|
||||
|
||||
return (ufs_setacl_posix1e(ap));
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_aclcheck_nfs4(struct vop_aclcheck_args *ap)
|
||||
{
|
||||
int is_directory = 0;
|
||||
|
||||
if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* With NFSv4 ACLs, chmod(2) may need to add additional entries.
|
||||
* Make sure it has enough room for that - splitting every entry
|
||||
* into two and appending "canonical six" entries at the end.
|
||||
*/
|
||||
if (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
|
||||
return (ENOSPC);
|
||||
|
||||
if (ap->a_vp->v_type == VDIR)
|
||||
is_directory = 1;
|
||||
|
||||
return (acl_nfs4_check(ap->a_aclp, is_directory));
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_aclcheck_posix1e(struct vop_aclcheck_args *ap)
|
||||
{
|
||||
|
||||
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
|
||||
return (EOPNOTSUPP);
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* Verify we understand this type of ACL, and that it applies
|
||||
@ -471,6 +681,12 @@ ufs_aclcheck(ap)
|
||||
} */ *ap;
|
||||
{
|
||||
|
||||
if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
if (ap->a_type == ACL_TYPE_NFS4)
|
||||
return (ufs_aclcheck_nfs4(ap));
|
||||
|
||||
return (ufs_aclcheck_posix1e(ap));
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,61 @@ SYSCTL_INT(_debug, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
|
||||
static int ufs_lookup_(struct vnode *, struct vnode **, struct componentname *,
|
||||
ino_t *);
|
||||
|
||||
static int
|
||||
ufs_delete_denied(struct vnode *vdp, struct vnode *tdp, struct ucred *cred,
|
||||
struct thread *td)
|
||||
{
|
||||
int error;
|
||||
|
||||
#ifdef UFS_ACL
|
||||
/*
|
||||
* NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt
|
||||
*
|
||||
* 3.16.2.1. ACE4_DELETE vs. ACE4_DELETE_CHILD
|
||||
*/
|
||||
|
||||
/*
|
||||
* XXX: Is this check required?
|
||||
*/
|
||||
error = VOP_ACCESS(vdp, VEXEC, cred, td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
error = VOP_ACCESSX(tdp, VDELETE, cred, td);
|
||||
if (error == 0)
|
||||
return (0);
|
||||
|
||||
error = VOP_ACCESSX(vdp, VDELETE_CHILD, cred, td);
|
||||
if (error == 0)
|
||||
return (0);
|
||||
|
||||
error = VOP_ACCESSX(vdp, VEXPLICIT_DENY | VDELETE_CHILD, cred, td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
#endif /* !UFS_ACL */
|
||||
|
||||
/*
|
||||
* Standard Unix access control - delete access requires VWRITE.
|
||||
*/
|
||||
error = VOP_ACCESS(vdp, VWRITE, cred, td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* If directory is "sticky", then user must own
|
||||
* the directory, or the file in it, else she
|
||||
* may not delete it (unless she's root). This
|
||||
* implements append-only directories.
|
||||
*/
|
||||
if ((VTOI(vdp)->i_mode & ISVTX) &&
|
||||
VOP_ACCESS(vdp, VADMIN, cred, td) &&
|
||||
VOP_ACCESS(tdp, VADMIN, cred, td))
|
||||
return (EPERM);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a component of a pathname into a pointer to a locked inode.
|
||||
* This is a very central and rather complicated routine.
|
||||
@ -410,8 +465,13 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
|
||||
/*
|
||||
* Access for write is interpreted as allowing
|
||||
* creation of files in the directory.
|
||||
*
|
||||
* XXX: Fix the comment above.
|
||||
*/
|
||||
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
|
||||
if (flags & WILLBEDIR)
|
||||
error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred, cnp->cn_thread);
|
||||
else
|
||||
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
|
||||
if (error)
|
||||
return (error);
|
||||
/*
|
||||
@ -498,12 +558,17 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
|
||||
if (nameiop == DELETE && (flags & ISLASTCN)) {
|
||||
if (flags & LOCKPARENT)
|
||||
ASSERT_VOP_ELOCKED(vdp, __FUNCTION__);
|
||||
/*
|
||||
* Write access to directory required to delete files.
|
||||
*/
|
||||
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
|
||||
if (error)
|
||||
if ((error = VFS_VGET(vdp->v_mount, ino,
|
||||
LK_EXCLUSIVE, &tdp)) != 0)
|
||||
return (error);
|
||||
|
||||
error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread);
|
||||
if (error) {
|
||||
vput(tdp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return pointer to current entry in dp->i_offset,
|
||||
* and distance past previous entry (if there
|
||||
@ -523,23 +588,10 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
|
||||
if (dp->i_number == ino) {
|
||||
VREF(vdp);
|
||||
*vpp = vdp;
|
||||
vput(tdp);
|
||||
return (0);
|
||||
}
|
||||
if ((error = VFS_VGET(vdp->v_mount, ino,
|
||||
LK_EXCLUSIVE, &tdp)) != 0)
|
||||
return (error);
|
||||
/*
|
||||
* If directory is "sticky", then user must own
|
||||
* the directory, or the file in it, else she
|
||||
* may not delete it (unless she's root). This
|
||||
* implements append-only directories.
|
||||
*/
|
||||
if ((dp->i_mode & ISVTX) &&
|
||||
VOP_ACCESS(vdp, VADMIN, cred, cnp->cn_thread) &&
|
||||
VOP_ACCESS(tdp, VADMIN, cred, cnp->cn_thread)) {
|
||||
vput(tdp);
|
||||
return (EPERM);
|
||||
}
|
||||
|
||||
*vpp = tdp;
|
||||
return (0);
|
||||
}
|
||||
@ -551,7 +603,11 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
|
||||
* regular file, or empty directory.
|
||||
*/
|
||||
if (nameiop == RENAME && (flags & ISLASTCN)) {
|
||||
if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)))
|
||||
if (flags & WILLBEDIR)
|
||||
error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred, cnp->cn_thread);
|
||||
else
|
||||
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
|
||||
if (error)
|
||||
return (error);
|
||||
/*
|
||||
* Careful about locking second inode.
|
||||
@ -563,6 +619,33 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
|
||||
if ((error = VFS_VGET(vdp->v_mount, ino,
|
||||
LK_EXCLUSIVE, &tdp)) != 0)
|
||||
return (error);
|
||||
|
||||
error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread);
|
||||
if (error) {
|
||||
vput(tdp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef SunOS_doesnt_do_that
|
||||
/*
|
||||
* The only purpose of this check is to return the correct
|
||||
* error. Assume that we want to rename directory "a"
|
||||
* to a file "b", and that we have no ACL_WRITE_DATA on
|
||||
* a containing directory, but we _do_ have ACL_APPEND_DATA.
|
||||
* In that case, the VOP_ACCESS check above will return 0,
|
||||
* and the operation will fail with ENOTDIR instead
|
||||
* of EACCESS.
|
||||
*/
|
||||
if (tdp->v_type == VDIR)
|
||||
error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred, cnp->cn_thread);
|
||||
else
|
||||
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
|
||||
if (error) {
|
||||
vput(tdp);
|
||||
return (error);
|
||||
}
|
||||
#endif
|
||||
|
||||
*vpp = tdp;
|
||||
cnp->cn_flags |= SAVENAME;
|
||||
return (0);
|
||||
|
@ -88,7 +88,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <ufs/ffs/ffs_extern.h>
|
||||
|
||||
static vop_access_t ufs_access;
|
||||
static vop_accessx_t ufs_accessx;
|
||||
static int ufs_chmod(struct vnode *, int, struct ucred *, struct thread *);
|
||||
static int ufs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct thread *);
|
||||
static vop_close_t ufs_close;
|
||||
@ -298,8 +298,8 @@ ufs_close(ap)
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_access(ap)
|
||||
struct vop_access_args /* {
|
||||
ufs_accessx(ap)
|
||||
struct vop_accessx_args /* {
|
||||
struct vnode *a_vp;
|
||||
accmode_t a_accmode;
|
||||
struct ucred *a_cred;
|
||||
@ -315,6 +315,7 @@ ufs_access(ap)
|
||||
#endif
|
||||
#ifdef UFS_ACL
|
||||
struct acl *acl;
|
||||
acl_type_t type;
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -322,7 +323,7 @@ ufs_access(ap)
|
||||
* unless the file is a socket, fifo, or a block or
|
||||
* character device resident on the filesystem.
|
||||
*/
|
||||
if (accmode & VWRITE) {
|
||||
if (accmode & VMODIFY_PERMS) {
|
||||
switch (vp->v_type) {
|
||||
case VDIR:
|
||||
case VLNK:
|
||||
@ -367,41 +368,63 @@ ufs_access(ap)
|
||||
}
|
||||
}
|
||||
|
||||
/* If immutable bit set, nobody gets to write it. */
|
||||
if ((accmode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT)))
|
||||
/*
|
||||
* If immutable bit set, nobody gets to write it. "& ~VADMIN_PERMS"
|
||||
* is here, because without it, * it would be impossible for the owner
|
||||
* to remove the IMMUTABLE flag.
|
||||
*/
|
||||
if ((accmode & (VMODIFY_PERMS & ~VADMIN_PERMS)) &&
|
||||
(ip->i_flags & (IMMUTABLE | SF_SNAPSHOT)))
|
||||
return (EPERM);
|
||||
|
||||
#ifdef UFS_ACL
|
||||
if ((vp->v_mount->mnt_flag & MNT_ACLS) != 0) {
|
||||
if ((vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) != 0) {
|
||||
if (vp->v_mount->mnt_flag & MNT_NFS4ACLS)
|
||||
type = ACL_TYPE_NFS4;
|
||||
else
|
||||
type = ACL_TYPE_ACCESS;
|
||||
|
||||
acl = acl_alloc(M_WAITOK);
|
||||
error = VOP_GETACL(vp, ACL_TYPE_ACCESS, acl, ap->a_cred,
|
||||
ap->a_td);
|
||||
if (type == ACL_TYPE_NFS4)
|
||||
error = ufs_getacl_nfs4_internal(vp, acl, ap->a_td);
|
||||
else
|
||||
error = VOP_GETACL(vp, type, acl, ap->a_cred, ap->a_td);
|
||||
switch (error) {
|
||||
case EOPNOTSUPP:
|
||||
error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
|
||||
ip->i_gid, ap->a_accmode, ap->a_cred, NULL);
|
||||
break;
|
||||
case 0:
|
||||
error = vaccess_acl_posix1e(vp->v_type, ip->i_uid,
|
||||
ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL);
|
||||
if (type == ACL_TYPE_NFS4) {
|
||||
error = vaccess_acl_nfs4(vp->v_type, ip->i_uid,
|
||||
ip->i_gid, acl, accmode, ap->a_cred, NULL);
|
||||
} else {
|
||||
error = vfs_unixify_accmode(&accmode);
|
||||
if (error == 0)
|
||||
error = vaccess_acl_posix1e(vp->v_type, ip->i_uid,
|
||||
ip->i_gid, acl, accmode, ap->a_cred, NULL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf(
|
||||
"ufs_access(): Error retrieving ACL on object (%d).\n",
|
||||
error);
|
||||
if (error != EOPNOTSUPP)
|
||||
printf(
|
||||
"ufs_accessx(): Error retrieving ACL on object (%d).\n",
|
||||
error);
|
||||
/*
|
||||
* XXX: Fall back until debugged. Should
|
||||
* eventually possibly log an error, and return
|
||||
* EPERM for safety.
|
||||
*/
|
||||
error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
|
||||
ip->i_gid, ap->a_accmode, ap->a_cred, NULL);
|
||||
error = vfs_unixify_accmode(&accmode);
|
||||
if (error == 0)
|
||||
error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
|
||||
ip->i_gid, accmode, ap->a_cred, NULL);
|
||||
}
|
||||
acl_free(acl);
|
||||
} else
|
||||
|
||||
return (error);
|
||||
}
|
||||
#endif /* !UFS_ACL */
|
||||
error = vfs_unixify_accmode(&accmode);
|
||||
if (error == 0)
|
||||
error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
|
||||
ap->a_accmode, ap->a_cred, NULL);
|
||||
accmode, ap->a_cred, NULL);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@ -608,11 +631,20 @@ ufs_setattr(ap)
|
||||
* check succeeds.
|
||||
*/
|
||||
if (vap->va_vaflags & VA_UTIMES_NULL) {
|
||||
error = VOP_ACCESS(vp, VADMIN, cred, td);
|
||||
/*
|
||||
* NFSv4.1, draft 21, 6.2.1.3.1, Discussion of Mask Attributes
|
||||
*
|
||||
* "A user having ACL_WRITE_DATA or ACL_WRITE_ATTRIBUTES
|
||||
* will be allowed to set the times [..] to the current
|
||||
* server time."
|
||||
*
|
||||
* XXX: Calling it four times seems a little excessive.
|
||||
*/
|
||||
error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td);
|
||||
if (error)
|
||||
error = VOP_ACCESS(vp, VWRITE, cred, td);
|
||||
} else
|
||||
error = VOP_ACCESS(vp, VADMIN, cred, td);
|
||||
error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td);
|
||||
if (error)
|
||||
return (error);
|
||||
if (vap->va_atime.tv_sec != VNOVAL)
|
||||
@ -652,6 +684,32 @@ ufs_setattr(ap)
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef UFS_ACL
|
||||
static int
|
||||
ufs_update_nfs4_acl_after_mode_change(struct vnode *vp, int mode,
|
||||
int file_owner_id, struct ucred *cred, struct thread *td)
|
||||
{
|
||||
int error;
|
||||
struct acl *aclp;
|
||||
|
||||
aclp = acl_alloc(M_WAITOK);
|
||||
error = ufs_getacl_nfs4_internal(vp, aclp, td);
|
||||
/*
|
||||
* We don't have to handle EOPNOTSUPP here, as the filesystem claims
|
||||
* it supports ACLs.
|
||||
*/
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
acl_nfs4_sync_acl_from_mode(aclp, mode, file_owner_id);
|
||||
error = ufs_setacl_nfs4_internal(vp, aclp, td);
|
||||
|
||||
out:
|
||||
acl_free(aclp);
|
||||
return (error);
|
||||
}
|
||||
#endif /* UFS_ACL */
|
||||
|
||||
/*
|
||||
* Mark this file's access time for update for vfs_mark_atime(). This
|
||||
* is called from execve() and mmap().
|
||||
@ -689,7 +747,7 @@ ufs_chmod(vp, mode, cred, td)
|
||||
* To modify the permissions on a file, must possess VADMIN
|
||||
* for that file.
|
||||
*/
|
||||
if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
|
||||
if ((error = VOP_ACCESSX(vp, VWRITE_ACL, cred, td)))
|
||||
return (error);
|
||||
/*
|
||||
* Privileged processes may set the sticky bit on non-directories,
|
||||
@ -706,11 +764,25 @@ ufs_chmod(vp, mode, cred, td)
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deny setting setuid if we are not the file owner.
|
||||
*/
|
||||
if ((mode & ISUID) && ip->i_uid != cred->cr_uid) {
|
||||
error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
ip->i_mode &= ~ALLPERMS;
|
||||
ip->i_mode |= (mode & ALLPERMS);
|
||||
DIP_SET(ip, i_mode, ip->i_mode);
|
||||
ip->i_flag |= IN_CHANGE;
|
||||
return (0);
|
||||
#ifdef UFS_ACL
|
||||
if ((vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)
|
||||
error = ufs_update_nfs4_acl_after_mode_change(vp, mode, ip->i_uid, cred, td);
|
||||
#endif
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -742,14 +814,14 @@ ufs_chown(vp, uid, gid, cred, td)
|
||||
* To modify the ownership of a file, must possess VADMIN for that
|
||||
* file.
|
||||
*/
|
||||
if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
|
||||
if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td)))
|
||||
return (error);
|
||||
/*
|
||||
* To change the owner of a file, or change the group of a file to a
|
||||
* group of which we are not a member, the caller must have
|
||||
* privilege.
|
||||
*/
|
||||
if ((uid != ip->i_uid ||
|
||||
if (((uid != ip->i_uid && uid != cred->cr_uid) ||
|
||||
(gid != ip->i_gid && !groupmember(gid, cred))) &&
|
||||
(error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0)))
|
||||
return (error);
|
||||
@ -1397,6 +1469,33 @@ ufs_rename(ap)
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef UFS_ACL
|
||||
static int
|
||||
ufs_do_nfs4_acl_inheritance(struct vnode *dvp, struct vnode *tvp,
|
||||
mode_t child_mode, struct ucred *cred, struct thread *td)
|
||||
{
|
||||
int error;
|
||||
struct acl *parent_aclp, *child_aclp;
|
||||
|
||||
parent_aclp = acl_alloc(M_WAITOK);
|
||||
child_aclp = acl_alloc(M_WAITOK | M_ZERO);
|
||||
|
||||
error = ufs_getacl_nfs4_internal(dvp, parent_aclp, td);
|
||||
if (error)
|
||||
goto out;
|
||||
acl_nfs4_compute_inherited_acl(parent_aclp, child_aclp,
|
||||
child_mode, VTOI(tvp)->i_uid, tvp->v_type == VDIR);
|
||||
error = ufs_setacl_nfs4_internal(tvp, child_aclp, td);
|
||||
if (error)
|
||||
goto out;
|
||||
out:
|
||||
acl_free(parent_aclp);
|
||||
acl_free(child_aclp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Mkdir system call
|
||||
*/
|
||||
@ -1630,6 +1729,13 @@ ufs_mkdir(ap)
|
||||
acl_free(dacl);
|
||||
dacl = acl = NULL;
|
||||
}
|
||||
|
||||
if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) {
|
||||
error = ufs_do_nfs4_acl_inheritance(dvp, tvp, dmode,
|
||||
cnp->cn_cred, cnp->cn_thread);
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
#endif /* !UFS_ACL */
|
||||
|
||||
/*
|
||||
@ -2117,6 +2223,7 @@ ufsfifo_pathconf(ap)
|
||||
|
||||
switch (ap->a_name) {
|
||||
case _PC_ACL_EXTENDED:
|
||||
case _PC_ACL_NFS4:
|
||||
case _PC_ACL_PATH_MAX:
|
||||
case _PC_MAC_PRESENT:
|
||||
return (ufs_pathconf(ap));
|
||||
@ -2169,9 +2276,21 @@ ufs_pathconf(ap)
|
||||
*ap->a_retval = 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case _PC_ACL_NFS4:
|
||||
#ifdef UFS_ACL
|
||||
if (ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS)
|
||||
*ap->a_retval = 1;
|
||||
else
|
||||
*ap->a_retval = 0;
|
||||
#else
|
||||
*ap->a_retval = 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case _PC_ACL_PATH_MAX:
|
||||
#ifdef UFS_ACL
|
||||
if (ap->a_vp->v_mount->mnt_flag & MNT_ACLS)
|
||||
if (ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS))
|
||||
*ap->a_retval = ACL_MAX_ENTRIES;
|
||||
else
|
||||
*ap->a_retval = 3;
|
||||
@ -2466,6 +2585,13 @@ ufs_makeinode(mode, dvp, vpp, cnp)
|
||||
}
|
||||
acl_free(acl);
|
||||
}
|
||||
|
||||
if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) {
|
||||
error = ufs_do_nfs4_acl_inheritance(dvp, tvp, mode,
|
||||
cnp->cn_cred, cnp->cn_thread);
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
#endif /* !UFS_ACL */
|
||||
ufs_makedirentry(ip, cnp, &newdir);
|
||||
error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL);
|
||||
@ -2496,7 +2622,7 @@ struct vop_vector ufs_vnodeops = {
|
||||
.vop_read = VOP_PANIC,
|
||||
.vop_reallocblks = VOP_PANIC,
|
||||
.vop_write = VOP_PANIC,
|
||||
.vop_access = ufs_access,
|
||||
.vop_accessx = ufs_accessx,
|
||||
.vop_bmap = ufs_bmap,
|
||||
.vop_cachedlookup = ufs_lookup,
|
||||
.vop_close = ufs_close,
|
||||
@ -2540,7 +2666,7 @@ struct vop_vector ufs_vnodeops = {
|
||||
struct vop_vector ufs_fifoops = {
|
||||
.vop_default = &fifo_specops,
|
||||
.vop_fsync = VOP_PANIC,
|
||||
.vop_access = ufs_access,
|
||||
.vop_accessx = ufs_accessx,
|
||||
.vop_close = ufsfifo_close,
|
||||
.vop_getattr = ufs_getattr,
|
||||
.vop_inactive = ufs_inactive,
|
||||
|
Loading…
Reference in New Issue
Block a user