summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/compat/linux/linux_file.c2
-rw-r--r--sys/kern/vfs_syscalls.c16
-rw-r--r--sys/sys/syscallsubr.h2
-rw-r--r--sys/ufs/ffs/ffs_alloc.c132
-rw-r--r--sys/ufs/ffs/fs.h5
-rw-r--r--sys/ufs/ufs/ufs_lookup.c5
6 files changed, 146 insertions, 16 deletions
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c
index bb49e3f..4e33eaa 100644
--- a/sys/compat/linux/linux_file.c
+++ b/sys/compat/linux/linux_file.c
@@ -652,7 +652,7 @@ linux_unlinkat(struct thread *td, struct linux_unlinkat_args *args)
if (args->flag & LINUX_AT_REMOVEDIR)
error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE);
else
- error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE);
+ error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE, 0);
if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) {
/* Introduce POSIX noncompliant behaviour of Linux */
if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path,
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index eb5fb87..aa072ff 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -1815,23 +1815,25 @@ unlinkat(struct thread *td, struct unlinkat_args *uap)
if (flag & AT_REMOVEDIR)
return (kern_rmdirat(td, fd, path, UIO_USERSPACE));
else
- return (kern_unlinkat(td, fd, path, UIO_USERSPACE));
+ return (kern_unlinkat(td, fd, path, UIO_USERSPACE, 0));
}
int
kern_unlink(struct thread *td, char *path, enum uio_seg pathseg)
{
- return (kern_unlinkat(td, AT_FDCWD, path, pathseg));
+ return (kern_unlinkat(td, AT_FDCWD, path, pathseg, 0));
}
int
-kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg)
+kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
+ ino_t oldinum)
{
struct mount *mp;
struct vnode *vp;
int error;
struct nameidata nd;
+ struct stat sb;
int vfslocked;
restart:
@@ -1842,9 +1844,13 @@ restart:
return (error == EINVAL ? EPERM : error);
vfslocked = NDHASGIANT(&nd);
vp = nd.ni_vp;
- if (vp->v_type == VDIR)
+ if (vp->v_type == VDIR && oldinum == 0) {
error = EPERM; /* POSIX */
- else {
+ } else if (oldinum != 0 &&
+ ((error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td)) == 0) &&
+ sb.st_ino != oldinum) {
+ error = EIDRM; /* Identifier removed */
+ } else {
/*
* The root of a mounted filesystem cannot be deleted.
*
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index 2813a58..35c0b26 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -212,7 +212,7 @@ int kern_truncate(struct thread *td, char *path, enum uio_seg pathseg,
off_t length);
int kern_unlink(struct thread *td, char *path, enum uio_seg pathseg);
int kern_unlinkat(struct thread *td, int fd, char *path,
- enum uio_seg pathseg);
+ enum uio_seg pathseg, ino_t oldinum);
int kern_utimes(struct thread *td, char *path, enum uio_seg pathseg,
struct timeval *tptr, enum uio_seg tptrseg);
int kern_utimesat(struct thread *td, int fd, char *path,
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index b4f001e..02e6df8 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>
@@ -2328,7 +2333,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 +2354,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 +2397,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 +2414,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 +2435,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 +2580,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 +2590,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 +2600,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 +2610,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 +2621,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 *)(int)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 *)(int)cmd.value,
+ UIO_USERSPACE, (ino_t)cmd.size);
+ break;
+
default:
#ifdef DEBUG
if (fsckcmds) {
@@ -2601,7 +2717,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/fs.h b/sys/ufs/ffs/fs.h
index 93c12a4..cc1d357 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.
diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c
index a19068e..b0247e7 100644
--- a/sys/ufs/ufs/ufs_lookup.c
+++ b/sys/ufs/ufs/ufs_lookup.c
@@ -1224,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;
OpenPOWER on IntegriCloud