1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Edward Tomasz Napierala under sponsorship 8 * from the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/buf.h> 40 #include <sys/ioccom.h> 41 #include <sys/mount.h> 42 #include <sys/vnode.h> 43 #include <sys/conf.h> 44 #include <sys/jail.h> 45 #include <sys/sx.h> 46 47 #include <security/mac/mac_framework.h> 48 49 #include <ufs/ufs/extattr.h> 50 #include <ufs/ufs/quota.h> 51 #include <ufs/ufs/ufsmount.h> 52 #include <ufs/ufs/inode.h> 53 54 #include <ufs/ffs/fs.h> 55 #include <ufs/ffs/ffs_extern.h> 56 57 static d_open_t ffs_susp_open; 58 static d_write_t ffs_susp_rdwr; 59 static d_ioctl_t ffs_susp_ioctl; 60 61 static struct cdevsw ffs_susp_cdevsw = { 62 .d_version = D_VERSION, 63 .d_open = ffs_susp_open, 64 .d_read = ffs_susp_rdwr, 65 .d_write = ffs_susp_rdwr, 66 .d_ioctl = ffs_susp_ioctl, 67 .d_name = "ffs_susp", 68 }; 69 70 static struct cdev *ffs_susp_dev; 71 static struct sx ffs_susp_lock; 72 73 static int 74 ffs_susp_suspended(struct mount *mp) 75 { 76 struct ufsmount *ump; 77 78 sx_assert(&ffs_susp_lock, SA_LOCKED); 79 80 ump = VFSTOUFS(mp); 81 if ((ump->um_flags & UM_WRITESUSPENDED) != 0) 82 return (1); 83 return (0); 84 } 85 86 static int 87 ffs_susp_open(struct cdev *dev __unused, int flags __unused, 88 int fmt __unused, struct thread *td __unused) 89 { 90 91 return (0); 92 } 93 94 static int 95 ffs_susp_rdwr(struct cdev *dev, struct uio *uio, int ioflag) 96 { 97 int error, i; 98 struct vnode *devvp; 99 struct mount *mp; 100 struct ufsmount *ump; 101 struct buf *bp; 102 void *base; 103 size_t len; 104 ssize_t cnt; 105 struct fs *fs; 106 107 sx_slock(&ffs_susp_lock); 108 109 error = devfs_get_cdevpriv((void **)&mp); 110 if (error != 0) { 111 sx_sunlock(&ffs_susp_lock); 112 return (ENXIO); 113 } 114 115 ump = VFSTOUFS(mp); 116 devvp = ump->um_devvp; 117 fs = ump->um_fs; 118 119 if (ffs_susp_suspended(mp) == 0) { 120 sx_sunlock(&ffs_susp_lock); 121 return (ENXIO); 122 } 123 124 KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE, 125 ("neither UIO_READ or UIO_WRITE")); 126 KASSERT(uio->uio_segflg == UIO_USERSPACE, 127 ("uio->uio_segflg != UIO_USERSPACE")); 128 129 cnt = uio->uio_resid; 130 131 for (i = 0; i < uio->uio_iovcnt; i++) { 132 while (uio->uio_iov[i].iov_len) { 133 base = uio->uio_iov[i].iov_base; 134 len = uio->uio_iov[i].iov_len; 135 if (len > fs->fs_bsize) 136 len = fs->fs_bsize; 137 if (fragoff(fs, uio->uio_offset) != 0 || 138 fragoff(fs, len) != 0) { 139 error = EINVAL; 140 goto out; 141 } 142 error = bread(devvp, btodb(uio->uio_offset), len, 143 NOCRED, &bp); 144 if (error != 0) 145 goto out; 146 if (uio->uio_rw == UIO_WRITE) { 147 error = copyin(base, bp->b_data, len); 148 if (error != 0) { 149 bp->b_flags |= B_INVAL | B_NOCACHE; 150 brelse(bp); 151 goto out; 152 } 153 error = bwrite(bp); 154 if (error != 0) 155 goto out; 156 } else { 157 error = copyout(bp->b_data, base, len); 158 brelse(bp); 159 if (error != 0) 160 goto out; 161 } 162 uio->uio_iov[i].iov_base = 163 (char *)uio->uio_iov[i].iov_base + len; 164 uio->uio_iov[i].iov_len -= len; 165 uio->uio_resid -= len; 166 uio->uio_offset += len; 167 } 168 } 169 170 out: 171 sx_sunlock(&ffs_susp_lock); 172 173 if (uio->uio_resid < cnt) 174 return (0); 175 176 return (error); 177 } 178 179 static int 180 ffs_susp_suspend(struct mount *mp) 181 { 182 struct ufsmount *ump; 183 int error; 184 185 sx_assert(&ffs_susp_lock, SA_XLOCKED); 186 187 if (!ffs_own_mount(mp)) 188 return (EINVAL); 189 if (ffs_susp_suspended(mp)) 190 return (EBUSY); 191 192 ump = VFSTOUFS(mp); 193 194 /* 195 * Make sure the calling thread is permitted to access the mounted 196 * device. The permissions can change after we unlock the vnode; 197 * it's harmless. 198 */ 199 vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); 200 error = VOP_ACCESS(ump->um_devvp, VREAD | VWRITE, 201 curthread->td_ucred, curthread); 202 VOP_UNLOCK(ump->um_devvp, 0); 203 if (error != 0) 204 return (error); 205 #ifdef MAC 206 if (mac_mount_check_stat(curthread->td_ucred, mp) != 0) 207 return (EPERM); 208 #endif 209 210 if ((error = vfs_write_suspend(mp, VS_SKIP_UNMOUNT)) != 0) 211 return (error); 212 213 UFS_LOCK(ump); 214 ump->um_flags |= UM_WRITESUSPENDED; 215 UFS_UNLOCK(ump); 216 217 return (0); 218 } 219 220 static void 221 ffs_susp_unsuspend(struct mount *mp) 222 { 223 struct ufsmount *ump; 224 225 sx_assert(&ffs_susp_lock, SA_XLOCKED); 226 227 /* 228 * XXX: The status is kept per-process; the vfs_write_resume() routine 229 * asserts that the resuming thread is the same one that called 230 * vfs_write_suspend(). The cdevpriv data, however, is attached 231 * to the file descriptor, e.g. is inherited during fork. Thus, 232 * it's possible that the resuming process will be different from 233 * the one that started the suspension. 234 * 235 * Work around by fooling the check in vfs_write_resume(). 236 */ 237 mp->mnt_susp_owner = curthread; 238 239 vfs_write_resume(mp, 0); 240 ump = VFSTOUFS(mp); 241 UFS_LOCK(ump); 242 ump->um_flags &= ~UM_WRITESUSPENDED; 243 UFS_UNLOCK(ump); 244 vfs_unbusy(mp); 245 } 246 247 static void 248 ffs_susp_dtor(void *data) 249 { 250 struct fs *fs; 251 struct ufsmount *ump; 252 struct mount *mp; 253 int error; 254 255 sx_xlock(&ffs_susp_lock); 256 257 mp = (struct mount *)data; 258 ump = VFSTOUFS(mp); 259 fs = ump->um_fs; 260 261 if (ffs_susp_suspended(mp) == 0) { 262 sx_xunlock(&ffs_susp_lock); 263 return; 264 } 265 266 KASSERT((mp->mnt_kern_flag & MNTK_SUSPEND) != 0, 267 ("MNTK_SUSPEND not set")); 268 269 error = ffs_reload(mp, curthread, FFSR_FORCE | FFSR_UNSUSPEND); 270 if (error != 0) 271 panic("failed to unsuspend writes on %s", fs->fs_fsmnt); 272 273 ffs_susp_unsuspend(mp); 274 sx_xunlock(&ffs_susp_lock); 275 } 276 277 static int 278 ffs_susp_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, 279 struct thread *td) 280 { 281 struct mount *mp; 282 fsid_t *fsidp; 283 int error; 284 285 /* 286 * No suspend inside the jail. Allowing it would require making 287 * sure that e.g. the devfs ruleset for that jail permits access 288 * to the devvp. 289 */ 290 if (jailed(td->td_ucred)) 291 return (EPERM); 292 293 sx_xlock(&ffs_susp_lock); 294 295 switch (cmd) { 296 case UFSSUSPEND: 297 fsidp = (fsid_t *)addr; 298 mp = vfs_getvfs(fsidp); 299 if (mp == NULL) { 300 error = ENOENT; 301 break; 302 } 303 error = vfs_busy(mp, 0); 304 vfs_rel(mp); 305 if (error != 0) 306 break; 307 error = ffs_susp_suspend(mp); 308 if (error != 0) { 309 vfs_unbusy(mp); 310 break; 311 } 312 error = devfs_set_cdevpriv(mp, ffs_susp_dtor); 313 if (error != 0) 314 ffs_susp_unsuspend(mp); 315 break; 316 case UFSRESUME: 317 error = devfs_get_cdevpriv((void **)&mp); 318 if (error != 0) 319 break; 320 /* 321 * This calls ffs_susp_dtor, which in turn unsuspends the fs. 322 * The dtor expects to be called without lock held, because 323 * sometimes it's called from here, and sometimes due to the 324 * file being closed or process exiting. 325 */ 326 sx_xunlock(&ffs_susp_lock); 327 devfs_clear_cdevpriv(); 328 return (0); 329 default: 330 error = ENXIO; 331 break; 332 } 333 334 sx_xunlock(&ffs_susp_lock); 335 336 return (error); 337 } 338 339 void 340 ffs_susp_initialize(void) 341 { 342 343 sx_init(&ffs_susp_lock, "ffs_susp"); 344 ffs_susp_dev = make_dev(&ffs_susp_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 345 "ufssuspend"); 346 } 347 348 void 349 ffs_susp_uninitialize(void) 350 { 351 352 destroy_dev(ffs_susp_dev); 353 sx_destroy(&ffs_susp_lock); 354 } 355