From 3592b7155cee9ffe3c786a4f434a76df763d0d1b Mon Sep 17 00:00:00 2001 From: Kirk McKusick Date: Wed, 26 Jul 2000 23:07:01 +0000 Subject: [PATCH] Clean up the snapshot code so that it no longer depends on the use of the SF_IMMUTABLE flag to prevent writing. Instead put in explicit checking for the SF_SNAPSHOT flag in the appropriate places. With this change, it is now possible to rename and link to snapshot files. It is also possible to set or clear any of the owner, group, or other read bits on the file, though none of the write or execute bits can be set. There is also an explicit test to prevent the setting or clearing of the SF_SNAPSHOT flag via chflags() or fchflags(). Note also that the modify time cannot be changed as it needs to accurately reflect the time that the snapshot was taken. Submitted by: Robert Watson --- sys/ufs/ffs/ffs_inode.c | 3 +++ sys/ufs/ffs/ffs_snapshot.c | 14 ++++++-------- sys/ufs/ufs/ufs_lookup.c | 14 ++++++++++++++ sys/ufs/ufs/ufs_vnops.c | 18 ++++++++++++++---- sys/vm/vm_mmap.c | 2 +- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c index 4725090aeb7..30f36ee7371 100644 --- a/sys/ufs/ffs/ffs_inode.c +++ b/sys/ufs/ffs/ffs_inode.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,8 @@ ffs_truncate(vp, length, flags, cred, p) if (error) return (error); #endif + if ((oip->i_flags & SF_SNAPSHOT) != 0) + ffs_snapremove(ovp); ovp->v_lasta = ovp->v_clen = ovp->v_cstart = ovp->v_lastw = 0; if (DOINGSOFTDEP(ovp)) { if (length > 0) { diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c index af03143975e..ba74b31de11 100644 --- a/sys/ufs/ffs/ffs_snapshot.c +++ b/sys/ufs/ffs/ffs_snapshot.c @@ -241,7 +241,7 @@ restart: /* * Change inode to snapshot type file. */ - ip->i_flags |= SF_IMMUTABLE | SF_SNAPSHOT; + ip->i_flags |= SF_SNAPSHOT; ip->i_flag |= IN_CHANGE | IN_UPDATE; /* * Ensure that the snapshot is completely on disk. @@ -414,7 +414,7 @@ restart: ino_to_fsbo(fs, xp->i_number); dip->di_size = 0; dip->di_blocks = 0; - dip->di_flags &= ~(SF_IMMUTABLE | SF_SNAPSHOT); + dip->di_flags &= ~SF_SNAPSHOT; bzero(&dip->di_db[0], (NDADDR + NIADDR) * sizeof(ufs_daddr_t)); nbp->b_flags |= B_VALIDSUSPWRT; bdwrite(nbp); @@ -636,11 +636,9 @@ ffs_snapremove(vp) ip->i_copyonwrite = 0; break; } - if (xp == 0) { + if (xp == 0) printf("ffs_snapremove: lost snapshot vnode %d\n", ip->i_number); - vref(vp); - } if (VTOI(devvp)->i_copyonwrite == 0) devvp->v_flag &= ~VCOPYONWRITE; /* @@ -673,9 +671,8 @@ ffs_snapremove(vp) /* * Clear snapshot flag and drop reference. */ - ip->i_flags &= ~(SF_IMMUTABLE | SF_SNAPSHOT); + ip->i_flags &= ~SF_SNAPSHOT; ip->i_flag |= IN_CHANGE | IN_UPDATE; - vrele(vp); } /* @@ -905,7 +902,8 @@ ffs_snapshot_unmount(mp) while ((xp = devip->i_copyonwrite) != 0) { devip->i_copyonwrite = xp->i_copyonwrite; xp->i_copyonwrite = 0; - vrele(ITOV(xp)); + if (xp->i_effnlink > 0) + vrele(ITOV(xp)); } ump->um_devvp->v_flag &= ~VCOPYONWRITE; } diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c index 3834a546e50..ee28d48dc15 100644 --- a/sys/ufs/ufs/ufs_lookup.c +++ b/sys/ufs/ufs/ufs_lookup.c @@ -923,6 +923,13 @@ out: error = bowrite(bp); } dp->i_flag |= IN_CHANGE | IN_UPDATE; + /* + * If the last named reference to a snapshot goes away, + * drop its snapshot reference so that it will be reclaimed + * when last open reference goes away. + */ + if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 && ip->i_effnlink == 0) + vrele(ITOV(ip)); return (error); } @@ -965,6 +972,13 @@ ufs_dirrewrite(dp, oip, newinum, newtype, isrmdir) } } dp->i_flag |= IN_CHANGE | IN_UPDATE; + /* + * If the last named reference to a snapshot goes away, + * drop its snapshot reference so that it will be reclaimed + * when last open reference goes away. + */ + if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_effnlink == 0) + vrele(ITOV(oip)); return (error); } diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index 0fac626ae70..0c2da7d50e2 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -331,7 +331,7 @@ ufs_access(ap) } /* If immutable bit set, nobody gets to write it. */ - if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) + if ((mode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) return (EPERM); /* Otherwise, user id 0 always gets access. */ @@ -454,6 +454,12 @@ ufs_setattr(ap) & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) && securelevel > 0) return (EPERM); + /* Snapshot flag cannot be set or cleared */ + if (((vap->va_flags & SF_SNAPSHOT) != 0 && + (ip->i_flags & SF_SNAPSHOT) == 0) || + ((vap->va_flags & SF_SNAPSHOT) == 0 && + (ip->i_flags & SF_SNAPSHOT) != 0)) + return (EPERM); ip->i_flags = vap->va_flags; } else { if (ip->i_flags @@ -491,6 +497,8 @@ ufs_setattr(ap) case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); + if ((ip->i_flags & SF_SNAPSHOT) != 0) + return (EPERM); break; default: break; @@ -498,10 +506,11 @@ ufs_setattr(ap) if ((error = UFS_TRUNCATE(vp, vap->va_size, 0, cred, p)) != 0) return (error); } - ip = VTOI(vp); if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); + if ((ip->i_flags & SF_SNAPSHOT) != 0) + return (EPERM); if (cred->cr_uid != ip->i_uid && (error = suser_xxx(cred, p, PRISON_ROOT)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || @@ -528,6 +537,9 @@ ufs_setattr(ap) if (vap->va_mode != (mode_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); + if ((ip->i_flags & SF_SNAPSHOT) != 0 && (vap->va_mode & + (S_IXUSR | S_IWUSR | S_IXGRP | S_IWGRP | S_IXOTH | S_IWOTH))) + return (EPERM); error = ufs_chmod(vp, (int)vap->va_mode, cred, p); } VN_KNOTE(vp, NOTE_ATTRIB); @@ -702,8 +714,6 @@ ufs_remove(ap) int error; ip = VTOI(vp); - if ((ip->i_flags & SF_SNAPSHOT) != 0) - ffs_snapremove(vp); if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || (VTOI(dvp)->i_flags & APPEND)) { error = EPERM; diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index b196d6f81a6..79b6b72e361 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -358,7 +358,7 @@ mmap(p, uap) p->p_ucred, p))) return (error); if ((va.va_flags & - (IMMUTABLE|APPEND)) == 0) + (SF_SNAPSHOT|IMMUTABLE|APPEND)) == 0) maxprot |= VM_PROT_WRITE; else if (prot & PROT_WRITE) return (EPERM);