summaryrefslogtreecommitdiffstats
path: root/sys/ufs
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2010-03-04 13:35:57 +0000
committerdes <des@FreeBSD.org>2010-03-04 13:35:57 +0000
commit834fb25a9ed2240101506d137b5be7d71c75f306 (patch)
tree4002c72cd1ed11909f7640bea343988cfcf63c5b /sys/ufs
parent98b742f57cafbed05c101e60cc131f5980f044d0 (diff)
parent787cf8d03f1c58ada088933408f30fd63de85bf2 (diff)
downloadFreeBSD-src-834fb25a9ed2240101506d137b5be7d71c75f306.zip
FreeBSD-src-834fb25a9ed2240101506d137b5be7d71c75f306.tar.gz
IFH@204581
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_alloc.c206
-rw-r--r--sys/ufs/ffs/ffs_rawread.c15
-rw-r--r--sys/ufs/ffs/ffs_snapshot.c2
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c47
-rw-r--r--sys/ufs/ffs/fs.h91
-rw-r--r--sys/ufs/ufs/acl.h2
-rw-r--r--sys/ufs/ufs/ufs_acl.c222
-rw-r--r--sys/ufs/ufs/ufs_dirhash.c1
-rw-r--r--sys/ufs/ufs/ufs_lookup.c132
-rw-r--r--sys/ufs/ufs/ufs_vnops.c533
10 files changed, 919 insertions, 332 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index b4f001e..7bf1177 100644
--- a/sys/ufs/ffs/ffs_alloc.c
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/conf.h>
+#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/priv.h>
@@ -76,9 +77,13 @@ __FBSDID("$FreeBSD$");
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/kernel.h>
+#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
+#include <security/audit/audit.h>
+
+#include <ufs/ufs/dir.h>
#include <ufs/ufs/extattr.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
@@ -88,24 +93,25 @@ __FBSDID("$FreeBSD$");
#include <ufs/ffs/fs.h>
#include <ufs/ffs/ffs_extern.h>
-typedef ufs2_daddr_t allocfcn_t(struct inode *ip, int cg, ufs2_daddr_t bpref,
+typedef ufs2_daddr_t allocfcn_t(struct inode *ip, u_int cg, ufs2_daddr_t bpref,
int size);
-static ufs2_daddr_t ffs_alloccg(struct inode *, int, ufs2_daddr_t, int);
+static ufs2_daddr_t ffs_alloccg(struct inode *, u_int, ufs2_daddr_t, int);
static ufs2_daddr_t
ffs_alloccgblk(struct inode *, struct buf *, ufs2_daddr_t);
#ifdef INVARIANTS
static int ffs_checkblk(struct inode *, ufs2_daddr_t, long);
#endif
-static ufs2_daddr_t ffs_clusteralloc(struct inode *, int, ufs2_daddr_t, int);
+static ufs2_daddr_t ffs_clusteralloc(struct inode *, u_int, ufs2_daddr_t, int);
static void ffs_clusteracct(struct ufsmount *, struct fs *, struct cg *,
ufs1_daddr_t, int);
static ino_t ffs_dirpref(struct inode *);
-static ufs2_daddr_t ffs_fragextend(struct inode *, int, ufs2_daddr_t, int, int);
+static ufs2_daddr_t ffs_fragextend(struct inode *, u_int, ufs2_daddr_t,
+ int, int);
static void ffs_fserr(struct fs *, ino_t, char *);
static ufs2_daddr_t ffs_hashalloc
- (struct inode *, int, ufs2_daddr_t, int, allocfcn_t *);
-static ufs2_daddr_t ffs_nodealloccg(struct inode *, int, ufs2_daddr_t, int);
+ (struct inode *, u_int, ufs2_daddr_t, int, allocfcn_t *);
+static ufs2_daddr_t ffs_nodealloccg(struct inode *, u_int, ufs2_daddr_t, int);
static ufs1_daddr_t ffs_mapsearch(struct fs *, struct cg *, ufs2_daddr_t, int);
static int ffs_reallocblks_ufs1(struct vop_reallocblks_args *);
static int ffs_reallocblks_ufs2(struct vop_reallocblks_args *);
@@ -140,7 +146,7 @@ ffs_alloc(ip, lbn, bpref, size, flags, cred, bnp)
struct fs *fs;
struct ufsmount *ump;
ufs2_daddr_t bno;
- int cg, reclaimed;
+ u_int cg, reclaimed;
static struct timeval lastfail;
static int curfail;
int64_t delta;
@@ -243,7 +249,8 @@ ffs_realloccg(ip, lbprev, bprev, bpref, osize, nsize, flags, cred, bpp)
struct fs *fs;
struct buf *bp;
struct ufsmount *ump;
- int cg, request, error, reclaimed;
+ u_int cg, request, reclaimed;
+ int error;
ufs2_daddr_t bno;
static struct timeval lastfail;
static int curfail;
@@ -425,8 +432,10 @@ nospace:
reclaimed = 1;
softdep_request_cleanup(fs, vp);
UFS_UNLOCK(ump);
- if (bp)
+ if (bp) {
brelse(bp);
+ bp = NULL;
+ }
UFS_LOCK(ump);
goto retry;
}
@@ -928,7 +937,8 @@ ffs_valloc(pvp, mode, cred, vpp)
struct timespec ts;
struct ufsmount *ump;
ino_t ino, ipref;
- int cg, error, error1;
+ u_int cg;
+ int error, error1;
static struct timeval lastfail;
static int curfail;
@@ -1038,11 +1048,11 @@ ffs_dirpref(pip)
struct inode *pip;
{
struct fs *fs;
- int cg, prefcg, dirsize, cgsize;
- int avgifree, avgbfree, avgndir, curdirsize;
- int minifree, minbfree, maxndir;
- int mincg, minndir;
- int maxcontigdirs;
+ u_int cg, prefcg, dirsize, cgsize;
+ u_int avgifree, avgbfree, avgndir, curdirsize;
+ u_int minifree, minbfree, maxndir;
+ u_int mincg, minndir;
+ u_int maxcontigdirs;
mtx_assert(UFS_MTX(pip->i_ump), MA_OWNED);
fs = pip->i_fs;
@@ -1166,8 +1176,8 @@ ffs_blkpref_ufs1(ip, lbn, indx, bap)
ufs1_daddr_t *bap;
{
struct fs *fs;
- int cg;
- int avgbfree, startcg;
+ u_int cg;
+ u_int avgbfree, startcg;
mtx_assert(UFS_MTX(ip->i_ump), MA_OWNED);
fs = ip->i_fs;
@@ -1216,8 +1226,8 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap)
ufs2_daddr_t *bap;
{
struct fs *fs;
- int cg;
- int avgbfree, startcg;
+ u_int cg;
+ u_int avgbfree, startcg;
mtx_assert(UFS_MTX(ip->i_ump), MA_OWNED);
fs = ip->i_fs;
@@ -1270,14 +1280,14 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap)
static ufs2_daddr_t
ffs_hashalloc(ip, cg, pref, size, allocator)
struct inode *ip;
- int cg;
+ u_int cg;
ufs2_daddr_t pref;
int size; /* size for data blocks, mode for inodes */
allocfcn_t *allocator;
{
struct fs *fs;
ufs2_daddr_t result;
- int i, icg = cg;
+ u_int i, icg = cg;
mtx_assert(UFS_MTX(ip->i_ump), MA_OWNED);
#ifdef INVARIANTS
@@ -1328,7 +1338,7 @@ ffs_hashalloc(ip, cg, pref, size, allocator)
static ufs2_daddr_t
ffs_fragextend(ip, cg, bprev, osize, nsize)
struct inode *ip;
- int cg;
+ u_int cg;
ufs2_daddr_t bprev;
int osize, nsize;
{
@@ -1411,7 +1421,7 @@ fail:
static ufs2_daddr_t
ffs_alloccg(ip, cg, bpref, size)
struct inode *ip;
- int cg;
+ u_int cg;
ufs2_daddr_t bpref;
int size;
{
@@ -1581,7 +1591,7 @@ gotit:
static ufs2_daddr_t
ffs_clusteralloc(ip, cg, bpref, len)
struct inode *ip;
- int cg;
+ u_int cg;
ufs2_daddr_t bpref;
int len;
{
@@ -1705,7 +1715,7 @@ fail:
static ufs2_daddr_t
ffs_nodealloccg(ip, cg, ipref, mode)
struct inode *ip;
- int cg;
+ u_int cg;
ufs2_daddr_t ipref;
int mode;
{
@@ -1806,7 +1816,7 @@ gotit:
bdwrite(bp);
if (ibp != NULL)
bawrite(ibp);
- return (cg * fs->fs_ipg + ipref);
+ return ((ino_t)(cg * fs->fs_ipg + ipref));
}
/*
@@ -1851,7 +1861,8 @@ ffs_blkfree(ump, fs, devvp, bno, size, inum)
struct buf *bp;
ufs1_daddr_t fragno, cgbno;
ufs2_daddr_t cgblkno;
- int i, cg, blk, frags, bbase;
+ int i, blk, frags, bbase;
+ u_int cg;
u_int8_t *blksfree;
struct cdev *dev;
@@ -2049,7 +2060,8 @@ ffs_freefile(ump, fs, devvp, ino, mode)
struct cg *cgp;
struct buf *bp;
ufs2_daddr_t cgbno;
- int error, cg;
+ int error;
+ u_int cg;
u_int8_t *inosused;
struct cdev *dev;
@@ -2063,7 +2075,7 @@ ffs_freefile(ump, fs, devvp, ino, mode)
dev = devvp->v_rdev;
cgbno = fsbtodb(fs, cgtod(fs, cg));
}
- if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg)
+ if (ino >= fs->fs_ipg * fs->fs_ncg)
panic("ffs_freefile: range: dev = %s, ino = %lu, fs = %s",
devtoname(dev), (u_long)ino, fs->fs_fsmnt);
if ((error = bread(devvp, cgbno, (int)fs->fs_cgsize, NOCRED, &bp))) {
@@ -2080,8 +2092,8 @@ ffs_freefile(ump, fs, devvp, ino, mode)
inosused = cg_inosused(cgp);
ino %= fs->fs_ipg;
if (isclr(inosused, ino)) {
- printf("dev = %s, ino = %lu, fs = %s\n", devtoname(dev),
- (u_long)ino + cg * fs->fs_ipg, fs->fs_fsmnt);
+ printf("dev = %s, ino = %u, fs = %s\n", devtoname(dev),
+ ino + cg * fs->fs_ipg, fs->fs_fsmnt);
if (fs->fs_ronly == 0)
panic("ffs_freefile: freeing free inode");
}
@@ -2116,7 +2128,8 @@ ffs_checkfreefile(fs, devvp, ino)
struct cg *cgp;
struct buf *bp;
ufs2_daddr_t cgbno;
- int ret, cg;
+ int ret;
+ u_int cg;
u_int8_t *inosused;
cg = ino_to_cg(fs, ino);
@@ -2127,7 +2140,7 @@ ffs_checkfreefile(fs, devvp, ino)
/* devvp is a normal disk device */
cgbno = fsbtodb(fs, cgtod(fs, cg));
}
- if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg)
+ if (ino >= fs->fs_ipg * fs->fs_ncg)
return (1);
if (bread(devvp, cgbno, (int)fs->fs_cgsize, NOCRED, &bp)) {
brelse(bp);
@@ -2328,7 +2341,7 @@ ffs_fserr(fs, inum, cp)
/*
* This function provides the capability for the fsck program to
- * update an active filesystem. Eleven operations are provided:
+ * update an active filesystem. Fourteen operations are provided:
*
* adjrefcnt(inode, amt) - adjusts the reference count on the
* specified inode by the specified amount. Under normal
@@ -2349,6 +2362,12 @@ ffs_fserr(fs, inum, cp)
* as in use.
* setflags(flags, set/clear) - the fs_flags field has the specified
* flags set (second parameter +1) or cleared (second parameter -1).
+ * setcwd(dirinode) - set the current directory to dirinode in the
+ * filesystem associated with the snapshot.
+ * setdotdot(oldvalue, newvalue) - Verify that the inode number for ".."
+ * in the current directory is oldvalue then change it to newvalue.
+ * unlink(nameptr, oldvalue) - Verify that the inode number associated
+ * with nameptr in the current directory is oldvalue then unlink it.
*/
static int sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS);
@@ -2386,6 +2405,15 @@ static SYSCTL_NODE(_vfs_ffs, FFS_BLK_FREE, freeblks, CTLFLAG_WR,
static SYSCTL_NODE(_vfs_ffs, FFS_SET_FLAGS, setflags, CTLFLAG_WR,
sysctl_ffs_fsck, "Change Filesystem Flags");
+static SYSCTL_NODE(_vfs_ffs, FFS_SET_CWD, setcwd, CTLFLAG_WR,
+ sysctl_ffs_fsck, "Set Current Working Directory");
+
+static SYSCTL_NODE(_vfs_ffs, FFS_SET_DOTDOT, setdotdot, CTLFLAG_WR,
+ sysctl_ffs_fsck, "Change Value of .. Entry");
+
+static SYSCTL_NODE(_vfs_ffs, FFS_UNLINK, unlink, CTLFLAG_WR,
+ sysctl_ffs_fsck, "Unlink a Duplicate Name");
+
#ifdef DEBUG
static int fsckcmds = 0;
SYSCTL_INT(_debug, OID_AUTO, fsckcmds, CTLFLAG_RW, &fsckcmds, 0, "");
@@ -2394,16 +2422,18 @@ SYSCTL_INT(_debug, OID_AUTO, fsckcmds, CTLFLAG_RW, &fsckcmds, 0, "");
static int
sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
{
+ struct thread *td = curthread;
struct fsck_cmd cmd;
struct ufsmount *ump;
- struct vnode *vp;
- struct inode *ip;
+ struct vnode *vp, *vpold, *dvp, *fdvp;
+ struct inode *ip, *dp;
struct mount *mp;
struct fs *fs;
ufs2_daddr_t blkno;
long blkcnt, blksize;
+ struct filedesc *fdp;
struct file *fp;
- int filetype, error;
+ int vfslocked, filetype, error;
if (req->newlen > sizeof cmd)
return (EBADRPC);
@@ -2413,15 +2443,20 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
return (ERPCMISMATCH);
if ((error = getvnode(curproc->p_fd, cmd.handle, &fp)) != 0)
return (error);
- vn_start_write(fp->f_data, &mp, V_WAIT);
+ vp = fp->f_data;
+ if (vp->v_type != VREG && vp->v_type != VDIR) {
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ vn_start_write(vp, &mp, V_WAIT);
if (mp == 0 || strncmp(mp->mnt_stat.f_fstypename, "ufs", MFSNAMELEN)) {
vn_finished_write(mp);
- fdrop(fp, curthread);
+ fdrop(fp, td);
return (EINVAL);
}
if (mp->mnt_flag & MNT_RDONLY) {
vn_finished_write(mp);
- fdrop(fp, curthread);
+ fdrop(fp, td);
return (EROFS);
}
ump = VFSTOUFS(mp);
@@ -2553,6 +2588,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
#endif /* DEBUG */
fs->fs_cstotal.cs_ndir += cmd.value;
break;
+
case FFS_ADJ_NBFREE:
#ifdef DEBUG
if (fsckcmds) {
@@ -2562,6 +2598,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
#endif /* DEBUG */
fs->fs_cstotal.cs_nbfree += cmd.value;
break;
+
case FFS_ADJ_NIFREE:
#ifdef DEBUG
if (fsckcmds) {
@@ -2571,6 +2608,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
#endif /* DEBUG */
fs->fs_cstotal.cs_nifree += cmd.value;
break;
+
case FFS_ADJ_NFFREE:
#ifdef DEBUG
if (fsckcmds) {
@@ -2580,6 +2618,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
#endif /* DEBUG */
fs->fs_cstotal.cs_nffree += cmd.value;
break;
+
case FFS_ADJ_NUMCLUSTERS:
#ifdef DEBUG
if (fsckcmds) {
@@ -2590,6 +2629,91 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
fs->fs_cstotal.cs_numclusters += cmd.value;
break;
+ case FFS_SET_CWD:
+#ifdef DEBUG
+ if (fsckcmds) {
+ printf("%s: set current directory to inode %jd\n",
+ mp->mnt_stat.f_mntonname, (intmax_t)cmd.value);
+ }
+#endif /* DEBUG */
+ if ((error = ffs_vget(mp, (ino_t)cmd.value, LK_SHARED, &vp)))
+ break;
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+ AUDIT_ARG_VNODE1(vp);
+ if ((error = change_dir(vp, td)) != 0) {
+ vput(vp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ break;
+ }
+ VOP_UNLOCK(vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+ fdp = td->td_proc->p_fd;
+ FILEDESC_XLOCK(fdp);
+ vpold = fdp->fd_cdir;
+ fdp->fd_cdir = vp;
+ FILEDESC_XUNLOCK(fdp);
+ vfslocked = VFS_LOCK_GIANT(vpold->v_mount);
+ vrele(vpold);
+ VFS_UNLOCK_GIANT(vfslocked);
+ break;
+
+ case FFS_SET_DOTDOT:
+#ifdef DEBUG
+ if (fsckcmds) {
+ printf("%s: change .. in cwd from %jd to %jd\n",
+ mp->mnt_stat.f_mntonname, (intmax_t)cmd.value,
+ (intmax_t)cmd.size);
+ }
+#endif /* DEBUG */
+ /*
+ * First we have to get and lock the parent directory
+ * to which ".." points.
+ */
+ error = ffs_vget(mp, (ino_t)cmd.value, LK_EXCLUSIVE, &fdvp);
+ if (error)
+ break;
+ /*
+ * Now we get and lock the child directory containing "..".
+ */
+ FILEDESC_SLOCK(td->td_proc->p_fd);
+ dvp = td->td_proc->p_fd->fd_cdir;
+ FILEDESC_SUNLOCK(td->td_proc->p_fd);
+ if ((error = vget(dvp, LK_EXCLUSIVE, td)) != 0) {
+ vput(fdvp);
+ break;
+ }
+ dp = VTOI(dvp);
+ dp->i_offset = 12; /* XXX mastertemplate.dot_reclen */
+ error = ufs_dirrewrite(dp, VTOI(fdvp), (ino_t)cmd.size,
+ DT_DIR, 0);
+ cache_purge(fdvp);
+ cache_purge(dvp);
+ vput(dvp);
+ vput(fdvp);
+ break;
+
+ case FFS_UNLINK:
+#ifdef DEBUG
+ if (fsckcmds) {
+ char buf[32];
+
+ if (copyinstr((char *)(intptr_t)cmd.value, buf,32,NULL))
+ strncpy(buf, "Name_too_long", 32);
+ printf("%s: unlink %s (inode %jd)\n",
+ mp->mnt_stat.f_mntonname, buf, (intmax_t)cmd.size);
+ }
+#endif /* DEBUG */
+ /*
+ * kern_unlinkat will do its own start/finish writes and
+ * they do not nest, so drop ours here. Setting mp == NULL
+ * indicates that vn_finished_write is not needed down below.
+ */
+ vn_finished_write(mp);
+ mp = NULL;
+ error = kern_unlinkat(td, AT_FDCWD, (char *)(intptr_t)cmd.value,
+ UIO_USERSPACE, (ino_t)cmd.size);
+ break;
+
default:
#ifdef DEBUG
if (fsckcmds) {
@@ -2601,7 +2725,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
break;
}
- fdrop(fp, curthread);
+ fdrop(fp, td);
vn_finished_write(mp);
return (error);
}
diff --git a/sys/ufs/ffs/ffs_rawread.c b/sys/ufs/ffs/ffs_rawread.c
index 434c833..574d89c 100644
--- a/sys/ufs/ffs/ffs_rawread.c
+++ b/sys/ufs/ffs/ffs_rawread.c
@@ -101,6 +101,7 @@ ffs_rawread_sync(struct vnode *vp)
int upgraded;
struct bufobj *bo;
struct mount *mp;
+ vm_object_t obj;
/* Check for dirty mmap, pending writes and dirty buffers */
bo = &vp->v_bufobj;
@@ -108,7 +109,8 @@ ffs_rawread_sync(struct vnode *vp)
VI_LOCK(vp);
if (bo->bo_numoutput > 0 ||
bo->bo_dirty.bv_cnt > 0 ||
- (vp->v_iflag & VI_OBJDIRTY) != 0) {
+ ((obj = vp->v_object) != NULL &&
+ (obj->flags & OBJ_MIGHTBEDIRTY) != 0)) {
VI_UNLOCK(vp);
BO_UNLOCK(bo);
@@ -138,13 +140,12 @@ ffs_rawread_sync(struct vnode *vp)
return (EIO);
}
/* Attempt to msync mmap() regions to clean dirty mmap */
- if ((vp->v_iflag & VI_OBJDIRTY) != 0) {
+ if ((obj = vp->v_object) != NULL &&
+ (obj->flags & OBJ_MIGHTBEDIRTY) != 0) {
VI_UNLOCK(vp);
- if (vp->v_object != NULL) {
- VM_OBJECT_LOCK(vp->v_object);
- vm_object_page_clean(vp->v_object, 0, 0, OBJPC_SYNC);
- VM_OBJECT_UNLOCK(vp->v_object);
- }
+ VM_OBJECT_LOCK(obj);
+ vm_object_page_clean(obj, 0, 0, OBJPC_SYNC);
+ VM_OBJECT_UNLOCK(obj);
} else
VI_UNLOCK(vp);
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index a6e73f5..b36cb58 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -739,7 +739,7 @@ out1:
auio.uio_iovcnt = 1;
aiov.iov_base = (void *)snapblklist;
aiov.iov_len = snaplistsize * sizeof(daddr_t);
- auio.uio_resid = aiov.iov_len;;
+ auio.uio_resid = aiov.iov_len;
auio.uio_offset = ip->i_size;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_WRITE;
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index d3d7c2c..8aa9f9c 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -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)
@@ -138,7 +138,7 @@ ffs_mount(struct mount *mp)
struct ufsmount *ump = 0;
struct fs *fs;
int error, flags;
- u_int mntorflags, mntandnotflags;
+ u_int mntorflags;
accmode_t accmode;
struct nameidata ndp;
char *fspec;
@@ -163,7 +163,6 @@ ffs_mount(struct mount *mp)
return (error);
mntorflags = 0;
- mntandnotflags = 0;
if (vfs_getopt(mp->mnt_optnew, "acls", NULL, NULL) == 0)
mntorflags |= MNT_ACLS;
@@ -177,8 +176,17 @@ 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;
+ mp->mnt_flag |= mntorflags;
MNT_IUNLOCK(mp);
/*
* If updating, check whether changing from read-only to
@@ -360,6 +368,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 +849,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 +863,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;
diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h
index 7da18ea..5452e2b 100644
--- a/sys/ufs/ffs/fs.h
+++ b/sys/ufs/ffs/fs.h
@@ -211,7 +211,10 @@
#define FFS_ADJ_NIFREE 9 /* adjust number of free inodes */
#define FFS_ADJ_NFFREE 10 /* adjust number of free frags */
#define FFS_ADJ_NUMCLUSTERS 11 /* adjust number of free clusters */
-#define FFS_MAXID 12 /* number of valid ffs ids */
+#define FFS_SET_CWD 12 /* set current directory */
+#define FFS_SET_DOTDOT 13 /* set inode number for ".." */
+#define FFS_UNLINK 14 /* remove a name in the filesystem */
+#define FFS_MAXID 15 /* number of valid ffs ids */
/*
* Command structure passed in to the filesystem to adjust filesystem values.
@@ -261,7 +264,7 @@ struct fs {
int32_t fs_old_time; /* last time written */
int32_t fs_old_size; /* number of blocks in fs */
int32_t fs_old_dsize; /* number of data blocks in fs */
- int32_t fs_ncg; /* number of cylinder groups */
+ u_int32_t fs_ncg; /* number of cylinder groups */
int32_t fs_bsize; /* size of basic blocks in fs */
int32_t fs_fsize; /* size of frag blocks in fs */
int32_t fs_frag; /* number of frags in a block in fs */
@@ -284,7 +287,7 @@ struct fs {
int32_t fs_spare1[2]; /* old fs_csmask */
/* old fs_csshift */
int32_t fs_nindir; /* value of NINDIR */
- int32_t fs_inopb; /* value of INOPB */
+ u_int32_t fs_inopb; /* value of INOPB */
int32_t fs_old_nspf; /* value of NSPF */
/* yet another configuration parameter */
int32_t fs_optim; /* optimization preference, see below */
@@ -301,7 +304,7 @@ struct fs {
int32_t fs_old_spc; /* sectors per cylinder */
int32_t fs_old_ncyl; /* cylinders in filesystem */
int32_t fs_old_cpg; /* cylinders per group */
- int32_t fs_ipg; /* inodes per group */
+ u_int32_t fs_ipg; /* inodes per group */
int32_t fs_fpg; /* blocks per group * fs_frag */
/* this data must be re-computed after crashes */
struct csum fs_old_cstotal; /* cylinder summary information */
@@ -332,10 +335,10 @@ struct fs {
int64_t fs_dsize; /* number of data blocks in fs */
ufs2_daddr_t fs_csaddr; /* blk addr of cyl grp summary area */
int64_t fs_pendingblocks; /* (u) blocks being freed */
- int32_t fs_pendinginodes; /* (u) inodes being freed */
- int32_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */
- int32_t fs_avgfilesize; /* expected average file size */
- int32_t fs_avgfpdir; /* expected # of files per directory */
+ u_int32_t fs_pendinginodes; /* (u) inodes being freed */
+ ino_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */
+ u_int32_t fs_avgfilesize; /* expected average file size */
+ u_int32_t fs_avgfpdir; /* expected # of files per directory */
int32_t fs_save_cgsize; /* save real cg size to use fs_bsize */
int32_t fs_sparecon32[26]; /* reserved for future constants */
int32_t fs_flags; /* see FS_ flags below */
@@ -393,22 +396,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.
- */
-#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 */
+ * 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 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.
@@ -458,26 +463,26 @@ struct cg {
int32_t cg_firstfield; /* historic cyl groups linked list */
int32_t cg_magic; /* magic number */
int32_t cg_old_time; /* time last written */
- int32_t cg_cgx; /* we are the cgx'th cylinder group */
+ u_int32_t cg_cgx; /* we are the cgx'th cylinder group */
int16_t cg_old_ncyl; /* number of cyl's this cg */
int16_t cg_old_niblk; /* number of inode blocks this cg */
- int32_t cg_ndblk; /* number of data blocks this cg */
- struct csum cg_cs; /* cylinder summary information */
- int32_t cg_rotor; /* position of last used block */
- int32_t cg_frotor; /* position of last used frag */
- int32_t cg_irotor; /* position of last used inode */
- int32_t cg_frsum[MAXFRAG]; /* counts of available frags */
+ u_int32_t cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ u_int32_t cg_rotor; /* position of last used block */
+ u_int32_t cg_frotor; /* position of last used frag */
+ u_int32_t cg_irotor; /* position of last used inode */
+ u_int32_t cg_frsum[MAXFRAG]; /* counts of available frags */
int32_t cg_old_btotoff; /* (int32) block totals per cylinder */
int32_t cg_old_boff; /* (u_int16) free block positions */
- int32_t cg_iusedoff; /* (u_int8) used inode map */
- int32_t cg_freeoff; /* (u_int8) free block map */
- int32_t cg_nextfreeoff; /* (u_int8) next available space */
- int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */
- int32_t cg_clusteroff; /* (u_int8) free cluster map */
- int32_t cg_nclusterblks; /* number of clusters this cg */
- int32_t cg_niblk; /* number of inode blocks this cg */
- int32_t cg_initediblk; /* last initialized inode */
- int32_t cg_unrefs; /* number of unreferenced inodes */
+ u_int32_t cg_iusedoff; /* (u_int8) used inode map */
+ u_int32_t cg_freeoff; /* (u_int8) free block map */
+ u_int32_t cg_nextfreeoff; /* (u_int8) next available space */
+ u_int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */
+ u_int32_t cg_clusteroff; /* (u_int8) free cluster map */
+ u_int32_t cg_nclusterblks; /* number of clusters this cg */
+ u_int32_t cg_niblk; /* number of inode blocks this cg */
+ u_int32_t cg_initediblk; /* last initialized inode */
+ u_int32_t cg_unrefs; /* number of unreferenced inodes */
int32_t cg_sparecon32[2]; /* reserved for future use */
ufs_time_t cg_time; /* time last written */
int64_t cg_sparecon64[3]; /* reserved for future use */
@@ -524,11 +529,11 @@ struct cg {
* inode number to cylinder group number.
* inode number to filesystem block address.
*/
-#define ino_to_cg(fs, x) ((x) / (fs)->fs_ipg)
+#define ino_to_cg(fs, x) (((ino_t)(x)) / (fs)->fs_ipg)
#define ino_to_fsba(fs, x) \
- ((ufs2_daddr_t)(cgimin(fs, ino_to_cg(fs, x)) + \
- (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs))))))
-#define ino_to_fsbo(fs, x) ((x) % INOPB(fs))
+ ((ufs2_daddr_t)(cgimin(fs, ino_to_cg(fs, (ino_t)(x))) + \
+ (blkstofrags((fs), ((((ino_t)(x)) % (fs)->fs_ipg) / INOPB(fs))))))
+#define ino_to_fsbo(fs, x) (((ino_t)(x)) % INOPB(fs))
/*
* Give cylinder group number for a filesystem block.
diff --git a/sys/ufs/ufs/acl.h b/sys/ufs/ufs/acl.h
index b53e328..235261e 100644
--- a/sys/ufs/ufs/acl.h
+++ b/sys/ufs/ufs/acl.h
@@ -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);
diff --git a/sys/ufs/ufs/ufs_acl.c b/sys/ufs/ufs/ufs_acl.c
index 68e5015..8ed1c0b 100644
--- a/sys/ufs/ufs/ufs_acl.c
+++ b/sys/ufs/ufs/ufs_acl.c
@@ -141,6 +141,81 @@ ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip)
}
/*
+ * 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,10 +357,118 @@ 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 (error);
+}
+
+/*
* Set the ACL on a file.
*
* As part of the ACL is stored in the inode, and the rest in an EA,
@@ -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));
}
diff --git a/sys/ufs/ufs/ufs_dirhash.c b/sys/ufs/ufs/ufs_dirhash.c
index e18b838..c85fdc8 100644
--- a/sys/ufs/ufs/ufs_dirhash.c
+++ b/sys/ufs/ufs/ufs_dirhash.c
@@ -632,6 +632,7 @@ restart:
goto fail;
}
}
+ KASSERT(bp != NULL, ("no buffer allocated"));
dp = (struct direct *)(bp->b_data + (offset & bmask));
if (dp->d_reclen == 0 || dp->d_reclen >
DIRBLKSIZ - (offset & (DIRBLKSIZ - 1))) {
diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c
index ea73b1f..b0247e7 100644
--- a/sys/ufs/ufs/ufs_lookup.c
+++ b/sys/ufs/ufs/ufs_lookup.c
@@ -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 @@ notfound:
/*
* 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 @@ found:
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 @@ found:
if (dp->i_number == ino) {
VREF(vdp);
*vpp = vdp;
- 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);
+ return (0);
}
+
*vpp = tdp;
return (0);
}
@@ -551,7 +603,11 @@ found:
* 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 @@ found:
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);
@@ -1141,6 +1224,11 @@ ufs_dirrewrite(dp, oip, newinum, newtype, isrmdir)
error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp);
if (error)
return (error);
+ if (ep->d_namlen == 2 && ep->d_name[1] == '.' && ep->d_name[0] == '.' &&
+ ep->d_ino != oip->i_number) {
+ brelse(bp);
+ return (EIDRM);
+ }
ep->d_ino = newinum;
if (!OFSFMT(vdp))
ep->d_type = newtype;
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index 08b77ae..9d4d93d 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -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 @@ relock:
}
}
- /* 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,190 @@ out:
return (error);
}
+#ifdef UFS_ACL
+static int
+ufs_do_posix1e_acl_inheritance_dir(struct vnode *dvp, struct vnode *tvp,
+ mode_t dmode, struct ucred *cred, struct thread *td)
+{
+ int error;
+ struct inode *ip = VTOI(tvp);
+ struct acl *dacl, *acl;
+
+ acl = acl_alloc(M_WAITOK);
+ dacl = acl_alloc(M_WAITOK);
+
+ /*
+ * Retrieve default ACL from parent, if any.
+ */
+ error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cred, td);
+ switch (error) {
+ case 0:
+ /*
+ * Retrieved a default ACL, so merge mode and ACL if
+ * necessary. If the ACL is empty, fall through to
+ * the "not defined or available" case.
+ */
+ if (acl->acl_cnt != 0) {
+ dmode = acl_posix1e_newfilemode(dmode, acl);
+ ip->i_mode = dmode;
+ DIP_SET(ip, i_mode, dmode);
+ *dacl = *acl;
+ ufs_sync_acl_from_inode(ip, acl);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case EOPNOTSUPP:
+ /*
+ * Just use the mode as-is.
+ */
+ ip->i_mode = dmode;
+ DIP_SET(ip, i_mode, dmode);
+ error = 0;
+ goto out;
+
+ default:
+ goto out;
+ }
+
+ /*
+ * XXX: If we abort now, will Soft Updates notify the extattr
+ * code that the EAs for the file need to be released?
+ */
+ error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cred, td);
+ if (error == 0)
+ error = VOP_SETACL(tvp, ACL_TYPE_DEFAULT, dacl, cred, td);
+ switch (error) {
+ case 0:
+ break;
+
+ case EOPNOTSUPP:
+ /*
+ * XXX: This should not happen, as EOPNOTSUPP above
+ * was supposed to free acl.
+ */
+ printf("ufs_mkdir: VOP_GETACL() but no VOP_SETACL()\n");
+ /*
+ panic("ufs_mkdir: VOP_GETACL() but no VOP_SETACL()");
+ */
+ break;
+
+ default:
+ goto out;
+ }
+
+out:
+ acl_free(acl);
+ acl_free(dacl);
+
+ return (error);
+}
+
+static int
+ufs_do_posix1e_acl_inheritance_file(struct vnode *dvp, struct vnode *tvp,
+ mode_t mode, struct ucred *cred, struct thread *td)
+{
+ int error;
+ struct inode *ip = VTOI(tvp);
+ struct acl *acl;
+
+ acl = acl_alloc(M_WAITOK);
+
+ /*
+ * Retrieve default ACL for parent, if any.
+ */
+ error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cred, td);
+ switch (error) {
+ case 0:
+ /*
+ * Retrieved a default ACL, so merge mode and ACL if
+ * necessary.
+ */
+ if (acl->acl_cnt != 0) {
+ /*
+ * Two possible ways for default ACL to not
+ * be present. First, the EA can be
+ * undefined, or second, the default ACL can
+ * be blank. If it's blank, fall through to
+ * the it's not defined case.
+ */
+ mode = acl_posix1e_newfilemode(mode, acl);
+ ip->i_mode = mode;
+ DIP_SET(ip, i_mode, mode);
+ ufs_sync_acl_from_inode(ip, acl);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case EOPNOTSUPP:
+ /*
+ * Just use the mode as-is.
+ */
+ ip->i_mode = mode;
+ DIP_SET(ip, i_mode, mode);
+ error = 0;
+ goto out;
+
+ default:
+ goto out;
+ }
+
+ /*
+ * XXX: If we abort now, will Soft Updates notify the extattr
+ * code that the EAs for the file need to be released?
+ */
+ error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cred, td);
+ switch (error) {
+ case 0:
+ break;
+
+ case EOPNOTSUPP:
+ /*
+ * XXX: This should not happen, as EOPNOTSUPP above was
+ * supposed to free acl.
+ */
+ printf("ufs_makeinode: VOP_GETACL() but no "
+ "VOP_SETACL()\n");
+ /* panic("ufs_makeinode: VOP_GETACL() but no "
+ "VOP_SETACL()"); */
+ break;
+
+ default:
+ goto out;
+ }
+
+out:
+ acl_free(acl);
+
+ return (error);
+}
+
+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
*/
@@ -1417,9 +1673,6 @@ ufs_mkdir(ap)
struct buf *bp;
struct dirtemplate dirtemplate, *dtp;
struct direct newdir;
-#ifdef UFS_ACL
- struct acl *acl, *dacl;
-#endif
int error, dmode;
long blkoff;
@@ -1508,59 +1761,8 @@ ufs_mkdir(ap)
#endif
#endif /* !SUIDDIR */
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
-#ifdef UFS_ACL
- acl = dacl = NULL;
- if ((dvp->v_mount->mnt_flag & MNT_ACLS) != 0) {
- acl = acl_alloc(M_WAITOK);
- dacl = acl_alloc(M_WAITOK);
-
- /*
- * Retrieve default ACL from parent, if any.
- */
- error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cnp->cn_cred,
- cnp->cn_thread);
- switch (error) {
- case 0:
- /*
- * Retrieved a default ACL, so merge mode and ACL if
- * necessary. If the ACL is empty, fall through to
- * the "not defined or available" case.
- */
- if (acl->acl_cnt != 0) {
- dmode = acl_posix1e_newfilemode(dmode, acl);
- ip->i_mode = dmode;
- DIP_SET(ip, i_mode, dmode);
- *dacl = *acl;
- ufs_sync_acl_from_inode(ip, acl);
- break;
- }
- /* FALLTHROUGH */
-
- case EOPNOTSUPP:
- /*
- * Just use the mode as-is.
- */
- ip->i_mode = dmode;
- DIP_SET(ip, i_mode, dmode);
- acl_free(acl);
- acl_free(dacl);
- dacl = acl = NULL;
- break;
-
- default:
- UFS_VFREE(tvp, ip->i_number, dmode);
- vput(tvp);
- acl_free(acl);
- acl_free(dacl);
- return (error);
- }
- } else {
-#endif /* !UFS_ACL */
- ip->i_mode = dmode;
- DIP_SET(ip, i_mode, dmode);
-#ifdef UFS_ACL
- }
-#endif
+ ip->i_mode = dmode;
+ DIP_SET(ip, i_mode, dmode);
tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
ip->i_effnlink = 2;
ip->i_nlink = 2;
@@ -1595,40 +1797,16 @@ ufs_mkdir(ap)
}
#endif
#ifdef UFS_ACL
- if (acl != NULL) {
- /*
- * XXX: If we abort now, will Soft Updates notify the extattr
- * code that the EAs for the file need to be released?
- */
- error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cnp->cn_cred,
- cnp->cn_thread);
- if (error == 0)
- error = VOP_SETACL(tvp, ACL_TYPE_DEFAULT, dacl,
- cnp->cn_cred, cnp->cn_thread);
- switch (error) {
- case 0:
- break;
-
- case EOPNOTSUPP:
- /*
- * XXX: This should not happen, as EOPNOTSUPP above
- * was supposed to free acl.
- */
- printf("ufs_mkdir: VOP_GETACL() but no VOP_SETACL()\n");
- /*
- panic("ufs_mkdir: VOP_GETACL() but no VOP_SETACL()");
- */
- break;
-
- default:
- acl_free(acl);
- acl_free(dacl);
- dacl = acl = NULL;
+ if (dvp->v_mount->mnt_flag & MNT_ACLS) {
+ error = ufs_do_posix1e_acl_inheritance_dir(dvp, tvp, dmode,
+ cnp->cn_cred, cnp->cn_thread);
+ if (error)
+ goto bad;
+ } else 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;
- }
- acl_free(acl);
- acl_free(dacl);
- dacl = acl = NULL;
}
#endif /* !UFS_ACL */
@@ -1691,12 +1869,6 @@ bad:
if (error == 0) {
*ap->a_vpp = tvp;
} else {
-#ifdef UFS_ACL
- if (acl != NULL)
- acl_free(acl);
- if (dacl != NULL)
- acl_free(dacl);
-#endif
dp->i_effnlink--;
dp->i_nlink--;
DIP_SET(dp, i_nlink, dp->i_nlink);
@@ -2117,6 +2289,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 +2342,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;
@@ -2268,9 +2453,6 @@ ufs_makeinode(mode, dvp, vpp, cnp)
struct inode *ip, *pdir;
struct direct newdir;
struct vnode *tvp;
-#ifdef UFS_ACL
- struct acl *acl;
-#endif
int error;
pdir = VTOI(dvp);
@@ -2350,62 +2532,8 @@ ufs_makeinode(mode, dvp, vpp, cnp)
#endif
#endif /* !SUIDDIR */
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
-#ifdef UFS_ACL
- acl = NULL;
- if ((dvp->v_mount->mnt_flag & MNT_ACLS) != 0) {
- acl = acl_alloc(M_WAITOK);
-
- /*
- * Retrieve default ACL for parent, if any.
- */
- error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cnp->cn_cred,
- cnp->cn_thread);
- switch (error) {
- case 0:
- /*
- * Retrieved a default ACL, so merge mode and ACL if
- * necessary.
- */
- if (acl->acl_cnt != 0) {
- /*
- * Two possible ways for default ACL to not
- * be present. First, the EA can be
- * undefined, or second, the default ACL can
- * be blank. If it's blank, fall through to
- * the it's not defined case.
- */
- mode = acl_posix1e_newfilemode(mode, acl);
- ip->i_mode = mode;
- DIP_SET(ip, i_mode, mode);
- ufs_sync_acl_from_inode(ip, acl);
- break;
- }
- /* FALLTHROUGH */
-
- case EOPNOTSUPP:
- /*
- * Just use the mode as-is.
- */
- ip->i_mode = mode;
- DIP_SET(ip, i_mode, mode);
- acl_free(acl);
- acl = NULL;
- break;
-
- default:
- UFS_VFREE(tvp, ip->i_number, mode);
- vput(tvp);
- acl_free(acl);
- acl = NULL;
- return (error);
- }
- } else {
-#endif
- ip->i_mode = mode;
- DIP_SET(ip, i_mode, mode);
-#ifdef UFS_ACL
- }
-#endif
+ ip->i_mode = mode;
+ DIP_SET(ip, i_mode, mode);
tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
ip->i_effnlink = 1;
ip->i_nlink = 1;
@@ -2438,33 +2566,16 @@ ufs_makeinode(mode, dvp, vpp, cnp)
}
#endif
#ifdef UFS_ACL
- if (acl != NULL) {
- /*
- * XXX: If we abort now, will Soft Updates notify the extattr
- * code that the EAs for the file need to be released?
- */
- error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cnp->cn_cred,
- cnp->cn_thread);
- switch (error) {
- case 0:
- break;
-
- case EOPNOTSUPP:
- /*
- * XXX: This should not happen, as EOPNOTSUPP above was
- * supposed to free acl.
- */
- printf("ufs_makeinode: VOP_GETACL() but no "
- "VOP_SETACL()\n");
- /* panic("ufs_makeinode: VOP_GETACL() but no "
- "VOP_SETACL()"); */
- break;
-
- default:
- acl_free(acl);
+ if (dvp->v_mount->mnt_flag & MNT_ACLS) {
+ error = ufs_do_posix1e_acl_inheritance_file(dvp, tvp, mode,
+ cnp->cn_cred, cnp->cn_thread);
+ if (error)
+ goto bad;
+ } else 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;
- }
- acl_free(acl);
}
#endif /* !UFS_ACL */
ufs_makedirentry(ip, cnp, &newdir);
@@ -2496,7 +2607,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 +2651,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,
OpenPOWER on IntegriCloud