Index: sys/sys/filio.h =================================================================== --- sys/sys/filio.h +++ sys/sys/filio.h @@ -61,4 +61,6 @@ #define FIOSEEKDATA _IOWR('f', 97, off_t) /* SEEK_DATA */ #define FIOSEEKHOLE _IOWR('f', 98, off_t) /* SEEK_HOLE */ +#define FIONEXTBOOT _IOW('f', 22, int) /* Set /boot/nextboot.conf */ + #endif /* !_SYS_FILIO_H_ */ Index: sys/ufs/ffs/ffs_inode.c =================================================================== --- sys/ufs/ffs/ffs_inode.c +++ sys/ufs/ffs/ffs_inode.c @@ -212,6 +212,8 @@ if (error) return (error); #endif + if ((ip->i_flag & IN_NEXTBOOT) != 0) + ufs_clear_nextboot(ip); /* * Historically clients did not have to specify which data * they were truncating. So, if not specified, we assume Index: sys/ufs/ffs/ffs_vfsops.c =================================================================== --- sys/ufs/ffs/ffs_vfsops.c +++ sys/ufs/ffs/ffs_vfsops.c @@ -1692,6 +1692,10 @@ ip->i_fs = fs; ip->i_dev = dev; ip->i_number = ino; + UFS_LOCK(ump); + if (ino == ump->um_nextboot_ino) + ip->i_flag |= IN_NEXTBOOT; + UFS_UNLOCK(ump); ip->i_ea_refs = 0; ip->i_nextclustercg = -1; #ifdef QUOTA Index: sys/ufs/ffs/ffs_vnops.c =================================================================== --- sys/ufs/ffs/ffs_vnops.c +++ sys/ufs/ffs/ffs_vnops.c @@ -710,6 +710,9 @@ if (vn_rlimit_fsize(vp, uio, uio->uio_td)) return (EFBIG); + if ((ip->i_flag & IN_NEXTBOOT) != 0) + ufs_clear_nextboot(ip); + resid = uio->uio_resid; osize = ip->i_size; if (seqcount > BA_SEQMAX) Index: sys/ufs/ufs/inode.h =================================================================== --- sys/ufs/ufs/inode.h +++ sys/ufs/ufs/inode.h @@ -131,6 +131,8 @@ #define IN_TRUNCATED 0x0800 /* Journaled truncation pending. */ +#define IN_NEXTBOOT 0x1000 /* Configured nextboot.conf file. */ + #define i_devvp i_ump->um_devvp #define i_umbufobj i_ump->um_bo #define i_dirhash i_un.dirhash Index: sys/ufs/ufs/ufs_extern.h =================================================================== --- sys/ufs/ufs/ufs_extern.h +++ sys/ufs/ufs/ufs_extern.h @@ -58,6 +58,7 @@ struct buf *, int *, int *); int ufs_fhtovp(struct mount *, struct ufid *, int, struct vnode **); int ufs_checkpath(ino_t, ino_t, struct inode *, struct ucred *, ino_t *); +void ufs_clear_nextboot(struct inode *); void ufs_dirbad(struct inode *, doff_t, char *); int ufs_dirbadentry(struct vnode *, struct direct *, int); int ufs_dirempty(struct inode *, ino_t, struct ucred *); Index: sys/ufs/ufs/ufs_vnops.c =================================================================== --- sys/ufs/ufs/ufs_vnops.c +++ sys/ufs/ufs/ufs_vnops.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -898,6 +899,18 @@ return (error); } +void +ufs_clear_nextboot(struct inode *ip) +{ + + KASSERT((ip->i_flag & IN_NEXTBOOT) != 0, ("%p not NEXTBOOT inode", ip)); + ip->i_flag &= ~IN_NEXTBOOT; + set_nextboot_info(NULL, NULL, (void *)(uintptr_t)ip->i_number); + UFS_LOCK(ip->i_ump); + ip->i_ump->um_nextboot_ino = 0; + UFS_UNLOCK(ip->i_ump); +} + static int ufs_remove(ap) struct vop_remove_args /* { @@ -923,8 +936,11 @@ ufs_gjournal_orphan(vp); #endif error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0); - if (ip->i_nlink <= 0) + if (ip->i_nlink <= 0) { vp->v_vflag |= VV_NOSYNC; + if ((ip->i_flag & IN_NEXTBOOT) != 0) + ufs_clear_nextboot(ip); + } if ((ip->i_flags & SF_SNAPSHOT) != 0) { /* * Avoid deadlock where another thread is trying to @@ -985,6 +1001,8 @@ error = EPERM; goto out; } + if (ip->i_flag & IN_NEXTBOOT) + ufs_clear_nextboot(ip); ip->i_effnlink++; ip->i_nlink++; DIP_SET(ip, i_nlink, ip->i_nlink); @@ -1323,6 +1341,11 @@ tdp->i_effnlink == 0) panic("Bad effnlink fip %p, fdp %p, tdp %p", fip, fdp, tdp); + if ((fip->i_flag & IN_NEXTBOOT) != 0) + ufs_clear_nextboot(fip); + if (tip != NULL && (tip->i_flag & IN_NEXTBOOT) != 0) + ufs_clear_nextboot(tip); + /* * 1) Bump link count while we're moving stuff * around. If we crash somewhere before @@ -2717,6 +2740,101 @@ } static int +ufs_set_nextboot(struct vnode *vp, int on, struct thread *td) +{ + struct ufsmount *ump; + struct dumperinfo di; + struct vnode *devvp; + daddr_t diskblkno; + struct inode *ip; + int error, after; + void *token; + bool locked; + + locked = false; + + /* Protected by file object's vnode reference */ + ip = VTOI(vp); + token = (void *)(uintptr_t)ip->i_number; + + if (on == 0) { + ufs_clear_nextboot(ip); + return (0); + } + + if ((vp->v_mount->mnt_flag & MNT_RDONLY) != 0) + return (EROFS); + + ump = VFSTOUFS(vp->v_mount); + UFS_LOCK(ump); + if (ump->um_nextboot_ino != 0) { + UFS_UNLOCK(ump); + return (EBUSY); + } + ump->um_nextboot_ino = ip->i_number; + UFS_UNLOCK(ump); + + /* Determine if underlying device supports dump-style IO */ + devvp = ip->i_devvp; + vn_lock(devvp, LK_SHARED | LK_RETRY); + error = VOP_OPEN(devvp, FREAD, td->td_ucred, td, NULL); + if (error != 0) { + VOP_UNLOCK(devvp, 0); + printf("XXX%s: VOP_OPEN: %d\n", __func__, error); + goto out; + } + + error = VOP_IOCTL(devvp, DIOCGDDBWRITER, &di, 0, td->td_ucred, + td); + (void)VOP_CLOSE(devvp, FREAD, td->td_ucred, td); + VOP_UNLOCK(devvp, 0); + if (error != 0) { + printf("XXX%s: devvp get ddbwriter: %d\n", __func__, error); + goto out; + } + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + locked = true; + + error = VOP_BMAP(vp, 0, NULL, &diskblkno, &after, NULL); + if (error != 0) + goto out; + if (diskblkno < 0) { + /* Can't use a file with a hole at zero. */ + error = ENOSPC; + goto out; + } + KASSERT(after >= 0, ("after: %d", after)); + + /* + * Adjust "media" offset to the offset of the file's blocks on the + * device. + */ + di.mediaoffset += dbtob(diskblkno); +#ifdef INVARIANTS + /* Shrink size down to match partition */ + di.mediasize -= dbtob(diskblkno); + KASSERT(devvp->v_bufobj.bo_bsize * (1 + after) <= di.mediasize, + ("media doesn't contain nextboot.conf blocks")); +#endif + di.mediasize = devvp->v_bufobj.bo_bsize * (1 + after); + + error = set_nextboot_info(&di, td, token); + if (error == 0) + ip->i_flag |= IN_NEXTBOOT; + +out: + if (locked) + VOP_UNLOCK(vp, 0); + if (error != 0) { + UFS_LOCK(ump); + ump->um_nextboot_ino = 0; + UFS_UNLOCK(ump); + } + return (error); +} + +static int ufs_ioctl(struct vop_ioctl_args *ap) { @@ -2725,6 +2843,9 @@ case FIOSEEKHOLE: return (vn_bmap_seekhole(ap->a_vp, ap->a_command, (off_t *)ap->a_data, ap->a_cred)); + case FIONEXTBOOT: + return (ufs_set_nextboot(ap->a_vp, *(int *)ap->a_data, + ap->a_td)); default: return (ENOTTY); } Index: sys/ufs/ufs/ufsmount.h =================================================================== --- sys/ufs/ufs/ufsmount.h +++ sys/ufs/ufs/ufsmount.h @@ -88,6 +88,7 @@ int um_writesuspended; /* suspension in progress */ u_int um_trim_inflight; struct taskqueue *um_trim_tq; + ino_t um_nextboot_ino; int (*um_balloc)(struct vnode *, off_t, int, struct ucred *, int, struct buf **); int (*um_blkatoff)(struct vnode *, off_t, char **, struct buf **);