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