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