| @@ -1,1179 +1,1179 @@ | | | @@ -1,1179 +1,1179 @@ |
1 | /* $NetBSD: ffs_snapshot.c,v 1.82.4.3 2010/03/28 17:28:33 snj Exp $ */ | | 1 | /* $NetBSD: ffs_snapshot.c,v 1.82.4.3.4.1 2011/01/07 03:14:46 matt Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright 2000 Marshall Kirk McKusick. All Rights Reserved. | | 4 | * Copyright 2000 Marshall Kirk McKusick. All Rights Reserved. |
5 | * | | 5 | * |
6 | * Further information about snapshots can be obtained from: | | 6 | * Further information about snapshots can be obtained from: |
7 | * | | 7 | * |
8 | * Marshall Kirk McKusick http://www.mckusick.com/softdep/ | | 8 | * Marshall Kirk McKusick http://www.mckusick.com/softdep/ |
9 | * 1614 Oxford Street mckusick@mckusick.com | | 9 | * 1614 Oxford Street mckusick@mckusick.com |
10 | * Berkeley, CA 94709-1608 +1-510-843-9542 | | 10 | * Berkeley, CA 94709-1608 +1-510-843-9542 |
11 | * USA | | 11 | * USA |
12 | * | | 12 | * |
13 | * Redistribution and use in source and binary forms, with or without | | 13 | * Redistribution and use in source and binary forms, with or without |
14 | * modification, are permitted provided that the following conditions | | 14 | * modification, are permitted provided that the following conditions |
15 | * are met: | | 15 | * are met: |
16 | * | | 16 | * |
17 | * 1. Redistributions of source code must retain the above copyright | | 17 | * 1. Redistributions of source code must retain the above copyright |
18 | * notice, this list of conditions and the following disclaimer. | | 18 | * notice, this list of conditions and the following disclaimer. |
19 | * 2. Redistributions in binary form must reproduce the above copyright | | 19 | * 2. Redistributions in binary form must reproduce the above copyright |
20 | * notice, this list of conditions and the following disclaimer in the | | 20 | * notice, this list of conditions and the following disclaimer in the |
21 | * documentation and/or other materials provided with the distribution. | | 21 | * documentation and/or other materials provided with the distribution. |
22 | * | | 22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY MARSHALL KIRK MCKUSICK ``AS IS'' AND ANY | | 23 | * THIS SOFTWARE IS PROVIDED BY MARSHALL KIRK MCKUSICK ``AS IS'' AND ANY |
24 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | | 24 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | | 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
26 | * DISCLAIMED. IN NO EVENT SHALL MARSHALL KIRK MCKUSICK BE LIABLE FOR | | 26 | * DISCLAIMED. IN NO EVENT SHALL MARSHALL KIRK MCKUSICK BE LIABLE FOR |
27 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 27 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 | * SUCH DAMAGE. | | 33 | * SUCH DAMAGE. |
34 | * | | 34 | * |
35 | * @(#)ffs_snapshot.c 8.11 (McKusick) 7/23/00 | | 35 | * @(#)ffs_snapshot.c 8.11 (McKusick) 7/23/00 |
36 | * | | 36 | * |
37 | * from FreeBSD: ffs_snapshot.c,v 1.79 2004/02/13 02:02:06 kuriyama Exp | | 37 | * from FreeBSD: ffs_snapshot.c,v 1.79 2004/02/13 02:02:06 kuriyama Exp |
38 | */ | | 38 | */ |
39 | | | 39 | |
40 | #include <sys/cdefs.h> | | 40 | #include <sys/cdefs.h> |
41 | __KERNEL_RCSID(0, "$NetBSD: ffs_snapshot.c,v 1.82.4.3 2010/03/28 17:28:33 snj Exp $"); | | 41 | __KERNEL_RCSID(0, "$NetBSD: ffs_snapshot.c,v 1.82.4.3.4.1 2011/01/07 03:14:46 matt Exp $"); |
42 | | | 42 | |
43 | #if defined(_KERNEL_OPT) | | 43 | #if defined(_KERNEL_OPT) |
44 | #include "opt_ffs.h" | | 44 | #include "opt_ffs.h" |
45 | #endif | | 45 | #endif |
46 | | | 46 | |
47 | #include <sys/param.h> | | 47 | #include <sys/param.h> |
48 | #include <sys/kernel.h> | | 48 | #include <sys/kernel.h> |
49 | #include <sys/systm.h> | | 49 | #include <sys/systm.h> |
50 | #include <sys/conf.h> | | 50 | #include <sys/conf.h> |
51 | #include <sys/buf.h> | | 51 | #include <sys/buf.h> |
52 | #include <sys/proc.h> | | 52 | #include <sys/proc.h> |
53 | #include <sys/namei.h> | | 53 | #include <sys/namei.h> |
54 | #include <sys/sched.h> | | 54 | #include <sys/sched.h> |
55 | #include <sys/stat.h> | | 55 | #include <sys/stat.h> |
56 | #include <sys/malloc.h> | | 56 | #include <sys/malloc.h> |
57 | #include <sys/mount.h> | | 57 | #include <sys/mount.h> |
58 | #include <sys/resource.h> | | 58 | #include <sys/resource.h> |
59 | #include <sys/resourcevar.h> | | 59 | #include <sys/resourcevar.h> |
60 | #include <sys/vnode.h> | | 60 | #include <sys/vnode.h> |
61 | #include <sys/kauth.h> | | 61 | #include <sys/kauth.h> |
62 | #include <sys/fstrans.h> | | 62 | #include <sys/fstrans.h> |
63 | #include <sys/wapbl.h> | | 63 | #include <sys/wapbl.h> |
64 | | | 64 | |
65 | #include <miscfs/specfs/specdev.h> | | 65 | #include <miscfs/specfs/specdev.h> |
66 | | | 66 | |
67 | #include <ufs/ufs/quota.h> | | 67 | #include <ufs/ufs/quota.h> |
68 | #include <ufs/ufs/ufsmount.h> | | 68 | #include <ufs/ufs/ufsmount.h> |
69 | #include <ufs/ufs/inode.h> | | 69 | #include <ufs/ufs/inode.h> |
70 | #include <ufs/ufs/ufs_extern.h> | | 70 | #include <ufs/ufs/ufs_extern.h> |
71 | #include <ufs/ufs/ufs_bswap.h> | | 71 | #include <ufs/ufs/ufs_bswap.h> |
72 | #include <ufs/ufs/ufs_wapbl.h> | | 72 | #include <ufs/ufs/ufs_wapbl.h> |
73 | | | 73 | |
74 | #include <ufs/ffs/fs.h> | | 74 | #include <ufs/ffs/fs.h> |
75 | #include <ufs/ffs/ffs_extern.h> | | 75 | #include <ufs/ffs/ffs_extern.h> |
76 | | | 76 | |
77 | #include <uvm/uvm.h> | | 77 | #include <uvm/uvm.h> |
78 | | | 78 | |
79 | struct snap_info { | | 79 | struct snap_info { |
80 | kmutex_t si_lock; /* Lock this snapinfo */ | | 80 | kmutex_t si_lock; /* Lock this snapinfo */ |
81 | kmutex_t si_snaplock; /* Snapshot vnode common lock */ | | 81 | kmutex_t si_snaplock; /* Snapshot vnode common lock */ |
82 | TAILQ_HEAD(inodelst, inode) si_snapshots; /* List of active snapshots */ | | 82 | TAILQ_HEAD(inodelst, inode) si_snapshots; /* List of active snapshots */ |
83 | daddr_t *si_snapblklist; /* Snapshot block hints list */ | | 83 | daddr_t *si_snapblklist; /* Snapshot block hints list */ |
84 | uint32_t si_gen; /* Incremented on change */ | | 84 | uint32_t si_gen; /* Incremented on change */ |
85 | }; | | 85 | }; |
86 | | | 86 | |
87 | #if !defined(FFS_NO_SNAPSHOT) | | 87 | #if !defined(FFS_NO_SNAPSHOT) |
88 | typedef int (*acctfunc_t) | | 88 | typedef int (*acctfunc_t) |
89 | (struct vnode *, void *, int, int, struct fs *, daddr_t, int); | | 89 | (struct vnode *, void *, int, int, struct fs *, daddr_t, int); |
90 | | | 90 | |
91 | static int snapshot_setup(struct mount *, struct vnode *); | | 91 | static int snapshot_setup(struct mount *, struct vnode *); |
92 | static int snapshot_copyfs(struct mount *, struct vnode *, void **); | | 92 | static int snapshot_copyfs(struct mount *, struct vnode *, void **); |
93 | static int snapshot_expunge(struct mount *, struct vnode *, | | 93 | static int snapshot_expunge(struct mount *, struct vnode *, |
94 | struct fs *, daddr_t *, daddr_t **); | | 94 | struct fs *, daddr_t *, daddr_t **); |
95 | static int snapshot_expunge_snap(struct mount *, struct vnode *, | | 95 | static int snapshot_expunge_snap(struct mount *, struct vnode *, |
96 | struct fs *, daddr_t); | | 96 | struct fs *, daddr_t); |
97 | static int snapshot_writefs(struct mount *, struct vnode *, void *); | | 97 | static int snapshot_writefs(struct mount *, struct vnode *, void *); |
98 | static int cgaccount(struct vnode *, int, int *); | | 98 | static int cgaccount(struct vnode *, int, int *); |
99 | static int cgaccount1(int, struct vnode *, void *, int); | | 99 | static int cgaccount1(int, struct vnode *, void *, int); |
100 | static int expunge(struct vnode *, struct inode *, struct fs *, | | 100 | static int expunge(struct vnode *, struct inode *, struct fs *, |
101 | acctfunc_t, int); | | 101 | acctfunc_t, int); |
102 | static int indiracct(struct vnode *, struct vnode *, int, daddr_t, | | 102 | static int indiracct(struct vnode *, struct vnode *, int, daddr_t, |
103 | daddr_t, daddr_t, daddr_t, daddr_t, struct fs *, acctfunc_t, int); | | 103 | daddr_t, daddr_t, daddr_t, daddr_t, struct fs *, acctfunc_t, int); |
104 | static int fullacct(struct vnode *, void *, int, int, struct fs *, | | 104 | static int fullacct(struct vnode *, void *, int, int, struct fs *, |
105 | daddr_t, int); | | 105 | daddr_t, int); |
106 | static int snapacct(struct vnode *, void *, int, int, struct fs *, | | 106 | static int snapacct(struct vnode *, void *, int, int, struct fs *, |
107 | daddr_t, int); | | 107 | daddr_t, int); |
108 | static int mapacct(struct vnode *, void *, int, int, struct fs *, | | 108 | static int mapacct(struct vnode *, void *, int, int, struct fs *, |
109 | daddr_t, int); | | 109 | daddr_t, int); |
110 | #endif /* !defined(FFS_NO_SNAPSHOT) */ | | 110 | #endif /* !defined(FFS_NO_SNAPSHOT) */ |
111 | | | 111 | |
112 | static int ffs_copyonwrite(void *, struct buf *, bool); | | 112 | static int ffs_copyonwrite(void *, struct buf *, bool); |
113 | static int snapblkaddr(struct vnode *, daddr_t, daddr_t *); | | 113 | static int snapblkaddr(struct vnode *, daddr_t, daddr_t *); |
114 | static int rwfsblk(struct vnode *, int, void *, daddr_t); | | 114 | static int rwfsblk(struct vnode *, int, void *, daddr_t); |
115 | static int syncsnap(struct vnode *); | | 115 | static int syncsnap(struct vnode *); |
116 | static int wrsnapblk(struct vnode *, void *, daddr_t); | | 116 | static int wrsnapblk(struct vnode *, void *, daddr_t); |
117 | | | 117 | |
118 | static inline bool is_active_snapshot(struct snap_info *, struct inode *); | | 118 | static inline bool is_active_snapshot(struct snap_info *, struct inode *); |
119 | static inline daddr_t db_get(struct inode *, int); | | 119 | static inline daddr_t db_get(struct inode *, int); |
120 | static inline void db_assign(struct inode *, int, daddr_t); | | 120 | static inline void db_assign(struct inode *, int, daddr_t); |
121 | static inline daddr_t ib_get(struct inode *, int); | | 121 | static inline daddr_t ib_get(struct inode *, int); |
122 | static inline void ib_assign(struct inode *, int, daddr_t); | | 122 | static inline void ib_assign(struct inode *, int, daddr_t); |
123 | static inline daddr_t idb_get(struct inode *, void *, int); | | 123 | static inline daddr_t idb_get(struct inode *, void *, int); |
124 | static inline void idb_assign(struct inode *, void *, int, daddr_t); | | 124 | static inline void idb_assign(struct inode *, void *, int, daddr_t); |
125 | | | 125 | |
126 | #ifdef DEBUG | | 126 | #ifdef DEBUG |
127 | static int snapdebug = 0; | | 127 | static int snapdebug = 0; |
128 | #endif | | 128 | #endif |
129 | | | 129 | |
130 | int | | 130 | int |
131 | ffs_snapshot_init(struct ufsmount *ump) | | 131 | ffs_snapshot_init(struct ufsmount *ump) |
132 | { | | 132 | { |
133 | struct snap_info *si; | | 133 | struct snap_info *si; |
134 | | | 134 | |
135 | si = ump->um_snapinfo = kmem_alloc(sizeof(*si), KM_SLEEP); | | 135 | si = ump->um_snapinfo = kmem_alloc(sizeof(*si), KM_SLEEP); |
136 | if (si == NULL) | | 136 | if (si == NULL) |
137 | return ENOMEM; | | 137 | return ENOMEM; |
138 | | | 138 | |
139 | TAILQ_INIT(&si->si_snapshots); | | 139 | TAILQ_INIT(&si->si_snapshots); |
140 | mutex_init(&si->si_lock, MUTEX_DEFAULT, IPL_NONE); | | 140 | mutex_init(&si->si_lock, MUTEX_DEFAULT, IPL_NONE); |
141 | mutex_init(&si->si_snaplock, MUTEX_DEFAULT, IPL_NONE); | | 141 | mutex_init(&si->si_snaplock, MUTEX_DEFAULT, IPL_NONE); |
142 | si->si_gen = 0; | | 142 | si->si_gen = 0; |
143 | si->si_snapblklist = NULL; | | 143 | si->si_snapblklist = NULL; |
144 | | | 144 | |
145 | return 0; | | 145 | return 0; |
146 | } | | 146 | } |
147 | | | 147 | |
148 | void | | 148 | void |
149 | ffs_snapshot_fini(struct ufsmount *ump) | | 149 | ffs_snapshot_fini(struct ufsmount *ump) |
150 | { | | 150 | { |
151 | struct snap_info *si; | | 151 | struct snap_info *si; |
152 | | | 152 | |
153 | si = ump->um_snapinfo; | | 153 | si = ump->um_snapinfo; |
154 | ump->um_snapinfo = NULL; | | 154 | ump->um_snapinfo = NULL; |
155 | | | 155 | |
156 | KASSERT(TAILQ_EMPTY(&si->si_snapshots)); | | 156 | KASSERT(TAILQ_EMPTY(&si->si_snapshots)); |
157 | mutex_destroy(&si->si_lock); | | 157 | mutex_destroy(&si->si_lock); |
158 | mutex_destroy(&si->si_snaplock); | | 158 | mutex_destroy(&si->si_snaplock); |
159 | KASSERT(si->si_snapblklist == NULL); | | 159 | KASSERT(si->si_snapblklist == NULL); |
160 | kmem_free(si, sizeof(*si)); | | 160 | kmem_free(si, sizeof(*si)); |
161 | } | | 161 | } |
162 | | | 162 | |
163 | /* | | 163 | /* |
164 | * Create a snapshot file and initialize it for the filesystem. | | 164 | * Create a snapshot file and initialize it for the filesystem. |
165 | * Vnode is locked on entry and return. | | 165 | * Vnode is locked on entry and return. |
166 | */ | | 166 | */ |
167 | int | | 167 | int |
168 | ffs_snapshot(struct mount *mp, struct vnode *vp, struct timespec *ctime) | | 168 | ffs_snapshot(struct mount *mp, struct vnode *vp, struct timespec *ctime) |
169 | { | | 169 | { |
170 | #if defined(FFS_NO_SNAPSHOT) | | 170 | #if defined(FFS_NO_SNAPSHOT) |
171 | return EOPNOTSUPP; | | 171 | return EOPNOTSUPP; |
172 | } | | 172 | } |
173 | #else /* defined(FFS_NO_SNAPSHOT) */ | | 173 | #else /* defined(FFS_NO_SNAPSHOT) */ |
174 | bool suspended = false; | | 174 | bool suspended = false; |
175 | bool snapshot_locked = false; | | 175 | bool snapshot_locked = false; |
176 | int error, redo = 0, snaploc; | | 176 | int error, redo = 0, snaploc; |
177 | void *sbbuf = NULL; | | 177 | void *sbbuf = NULL; |
178 | daddr_t *snaplist = NULL, snaplistsize = 0; | | 178 | daddr_t *snaplist = NULL, snaplistsize = 0; |
179 | struct buf *bp, *nbp; | | 179 | struct buf *bp, *nbp; |
180 | struct fs *copy_fs, *fs = VFSTOUFS(mp)->um_fs; | | 180 | struct fs *copy_fs = NULL, *fs = VFSTOUFS(mp)->um_fs; |
181 | struct inode *ip = VTOI(vp); | | 181 | struct inode *ip = VTOI(vp); |
182 | struct lwp *l = curlwp; | | 182 | struct lwp *l = curlwp; |
183 | struct snap_info *si = VFSTOUFS(mp)->um_snapinfo; | | 183 | struct snap_info *si = VFSTOUFS(mp)->um_snapinfo; |
184 | struct timespec ts; | | 184 | struct timespec ts; |
185 | struct timeval starttime; | | 185 | struct timeval starttime; |
186 | #ifdef DEBUG | | 186 | #ifdef DEBUG |
187 | struct timeval endtime; | | 187 | struct timeval endtime; |
188 | #endif | | 188 | #endif |
189 | struct vnode *devvp = ip->i_devvp; | | 189 | struct vnode *devvp = ip->i_devvp; |
190 | | | 190 | |
191 | /* | | 191 | /* |
192 | * If the vnode already is a snapshot, return. | | 192 | * If the vnode already is a snapshot, return. |
193 | */ | | 193 | */ |
194 | if (VTOI(vp)->i_flags & SF_SNAPSHOT) { | | 194 | if (VTOI(vp)->i_flags & SF_SNAPSHOT) { |
195 | if (ctime) { | | 195 | if (ctime) { |
196 | ctime->tv_sec = DIP(VTOI(vp), mtime); | | 196 | ctime->tv_sec = DIP(VTOI(vp), mtime); |
197 | ctime->tv_nsec = DIP(VTOI(vp), mtimensec); | | 197 | ctime->tv_nsec = DIP(VTOI(vp), mtimensec); |
198 | } | | 198 | } |
199 | return 0; | | 199 | return 0; |
200 | } | | 200 | } |
201 | /* | | 201 | /* |
202 | * Check for free snapshot slot in the superblock. | | 202 | * Check for free snapshot slot in the superblock. |
203 | */ | | 203 | */ |
204 | for (snaploc = 0; snaploc < FSMAXSNAP; snaploc++) | | 204 | for (snaploc = 0; snaploc < FSMAXSNAP; snaploc++) |
205 | if (fs->fs_snapinum[snaploc] == 0) | | 205 | if (fs->fs_snapinum[snaploc] == 0) |
206 | break; | | 206 | break; |
207 | if (snaploc == FSMAXSNAP) | | 207 | if (snaploc == FSMAXSNAP) |
208 | return (ENOSPC); | | 208 | return (ENOSPC); |
209 | /* | | 209 | /* |
210 | * Prepare the vnode to become a snapshot. | | 210 | * Prepare the vnode to become a snapshot. |
211 | */ | | 211 | */ |
212 | error = snapshot_setup(mp, vp); | | 212 | error = snapshot_setup(mp, vp); |
213 | if (error) | | 213 | if (error) |
214 | goto out; | | 214 | goto out; |
215 | /* | | 215 | /* |
216 | * Change inode to snapshot type file. | | 216 | * Change inode to snapshot type file. |
217 | */ | | 217 | */ |
218 | ip->i_flags |= SF_SNAPSHOT; | | 218 | ip->i_flags |= SF_SNAPSHOT; |
219 | DIP_ASSIGN(ip, flags, ip->i_flags); | | 219 | DIP_ASSIGN(ip, flags, ip->i_flags); |
220 | ip->i_flag |= IN_CHANGE | IN_UPDATE; | | 220 | ip->i_flag |= IN_CHANGE | IN_UPDATE; |
221 | /* | | 221 | /* |
222 | * Copy all the cylinder group maps. Although the | | 222 | * Copy all the cylinder group maps. Although the |
223 | * filesystem is still active, we hope that only a few | | 223 | * filesystem is still active, we hope that only a few |
224 | * cylinder groups will change between now and when we | | 224 | * cylinder groups will change between now and when we |
225 | * suspend operations. Thus, we will be able to quickly | | 225 | * suspend operations. Thus, we will be able to quickly |
226 | * touch up the few cylinder groups that changed during | | 226 | * touch up the few cylinder groups that changed during |
227 | * the suspension period. | | 227 | * the suspension period. |
228 | */ | | 228 | */ |
229 | error = cgaccount(vp, 1, NULL); | | 229 | error = cgaccount(vp, 1, NULL); |
230 | if (error) | | 230 | if (error) |
231 | goto out; | | 231 | goto out; |
232 | /* | | 232 | /* |
233 | * Ensure that the snapshot is completely on disk. | | 233 | * Ensure that the snapshot is completely on disk. |
234 | * Since we have marked it as a snapshot it is safe to | | 234 | * Since we have marked it as a snapshot it is safe to |
235 | * unlock it as no process will be allowed to write to it. | | 235 | * unlock it as no process will be allowed to write to it. |
236 | */ | | 236 | */ |
237 | error = VOP_FSYNC(vp, l->l_cred, FSYNC_WAIT, 0, 0); | | 237 | error = VOP_FSYNC(vp, l->l_cred, FSYNC_WAIT, 0, 0); |
238 | if (error) | | 238 | if (error) |
239 | goto out; | | 239 | goto out; |
240 | VOP_UNLOCK(vp, 0); | | 240 | VOP_UNLOCK(vp, 0); |
241 | /* | | 241 | /* |
242 | * All allocations are done, so we can now suspend the filesystem. | | 242 | * All allocations are done, so we can now suspend the filesystem. |
243 | */ | | 243 | */ |
244 | error = vfs_suspend(vp->v_mount, 0); | | 244 | error = vfs_suspend(vp->v_mount, 0); |
245 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | | 245 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
246 | if (error) | | 246 | if (error) |
247 | goto out; | | 247 | goto out; |
248 | suspended = true; | | 248 | suspended = true; |
249 | getmicrotime(&starttime); | | 249 | getmicrotime(&starttime); |
250 | /* | | 250 | /* |
251 | * First, copy all the cylinder group maps that have changed. | | 251 | * First, copy all the cylinder group maps that have changed. |
252 | */ | | 252 | */ |
253 | error = cgaccount(vp, 2, &redo); | | 253 | error = cgaccount(vp, 2, &redo); |
254 | if (error) | | 254 | if (error) |
255 | goto out; | | 255 | goto out; |
256 | /* | | 256 | /* |
257 | * Create a copy of the superblock and its summary information. | | 257 | * Create a copy of the superblock and its summary information. |
258 | */ | | 258 | */ |
259 | error = snapshot_copyfs(mp, vp, &sbbuf); | | 259 | error = snapshot_copyfs(mp, vp, &sbbuf); |
260 | copy_fs = (struct fs *)((char *)sbbuf + blkoff(fs, fs->fs_sblockloc)); | | 260 | copy_fs = (struct fs *)((char *)sbbuf + blkoff(fs, fs->fs_sblockloc)); |
261 | if (error) | | 261 | if (error) |
262 | goto out; | | 262 | goto out; |
263 | /* | | 263 | /* |
264 | * Expunge unlinked files from our view. | | 264 | * Expunge unlinked files from our view. |
265 | */ | | 265 | */ |
266 | error = snapshot_expunge(mp, vp, copy_fs, &snaplistsize, &snaplist); | | 266 | error = snapshot_expunge(mp, vp, copy_fs, &snaplistsize, &snaplist); |
267 | if (error) | | 267 | if (error) |
268 | goto out; | | 268 | goto out; |
269 | /* | | 269 | /* |
270 | * Acquire the snapshot lock. | | 270 | * Acquire the snapshot lock. |
271 | */ | | 271 | */ |
272 | mutex_enter(&si->si_snaplock); | | 272 | mutex_enter(&si->si_snaplock); |
273 | snapshot_locked = true; | | 273 | snapshot_locked = true; |
274 | /* | | 274 | /* |
275 | * Record snapshot inode. Since this is the newest snapshot, | | 275 | * Record snapshot inode. Since this is the newest snapshot, |
276 | * it must be placed at the end of the list. | | 276 | * it must be placed at the end of the list. |
277 | */ | | 277 | */ |
278 | fs->fs_snapinum[snaploc] = ip->i_number; | | 278 | fs->fs_snapinum[snaploc] = ip->i_number; |
279 | | | 279 | |
280 | mutex_enter(&si->si_lock); | | 280 | mutex_enter(&si->si_lock); |
281 | if (is_active_snapshot(si, ip)) | | 281 | if (is_active_snapshot(si, ip)) |
282 | panic("ffs_snapshot: %"PRIu64" already on list", ip->i_number); | | 282 | panic("ffs_snapshot: %"PRIu64" already on list", ip->i_number); |
283 | TAILQ_INSERT_TAIL(&si->si_snapshots, ip, i_nextsnap); | | 283 | TAILQ_INSERT_TAIL(&si->si_snapshots, ip, i_nextsnap); |
284 | if (TAILQ_FIRST(&si->si_snapshots) == ip) { | | 284 | if (TAILQ_FIRST(&si->si_snapshots) == ip) { |
285 | /* | | 285 | /* |
286 | * If this is the first snapshot on this filesystem, put the | | 286 | * If this is the first snapshot on this filesystem, put the |
287 | * preliminary list in place and establish the cow handler. | | 287 | * preliminary list in place and establish the cow handler. |
288 | */ | | 288 | */ |
289 | si->si_snapblklist = snaplist; | | 289 | si->si_snapblklist = snaplist; |
290 | fscow_establish(mp, ffs_copyonwrite, devvp); | | 290 | fscow_establish(mp, ffs_copyonwrite, devvp); |
291 | } | | 291 | } |
292 | si->si_gen++; | | 292 | si->si_gen++; |
293 | mutex_exit(&si->si_lock); | | 293 | mutex_exit(&si->si_lock); |
294 | | | 294 | |
295 | vp->v_vflag |= VV_SYSTEM; | | 295 | vp->v_vflag |= VV_SYSTEM; |
296 | /* | | 296 | /* |
297 | * Set the mtime to the time the snapshot has been taken. | | 297 | * Set the mtime to the time the snapshot has been taken. |
298 | */ | | 298 | */ |
299 | TIMEVAL_TO_TIMESPEC(&starttime, &ts); | | 299 | TIMEVAL_TO_TIMESPEC(&starttime, &ts); |
300 | if (ctime) | | 300 | if (ctime) |
301 | *ctime = ts; | | 301 | *ctime = ts; |
302 | DIP_ASSIGN(ip, mtime, ts.tv_sec); | | 302 | DIP_ASSIGN(ip, mtime, ts.tv_sec); |
303 | DIP_ASSIGN(ip, mtimensec, ts.tv_nsec); | | 303 | DIP_ASSIGN(ip, mtimensec, ts.tv_nsec); |
304 | ip->i_flag |= IN_CHANGE | IN_UPDATE; | | 304 | ip->i_flag |= IN_CHANGE | IN_UPDATE; |
305 | /* | | 305 | /* |
306 | * Copy allocation information from all snapshots and then | | 306 | * Copy allocation information from all snapshots and then |
307 | * expunge them from our view. | | 307 | * expunge them from our view. |
308 | */ | | 308 | */ |
309 | error = snapshot_expunge_snap(mp, vp, copy_fs, snaplistsize); | | 309 | error = snapshot_expunge_snap(mp, vp, copy_fs, snaplistsize); |
310 | if (error) | | 310 | if (error) |
311 | goto out; | | 311 | goto out; |
312 | /* | | 312 | /* |
313 | * Write the superblock and its summary information to the snapshot. | | 313 | * Write the superblock and its summary information to the snapshot. |
314 | */ | | 314 | */ |
315 | error = snapshot_writefs(mp, vp, sbbuf); | | 315 | error = snapshot_writefs(mp, vp, sbbuf); |
316 | if (error) | | 316 | if (error) |
317 | goto out; | | 317 | goto out; |
318 | /* | | 318 | /* |
319 | * We're nearly done, ensure that the snapshot is completely on disk. | | 319 | * We're nearly done, ensure that the snapshot is completely on disk. |
320 | */ | | 320 | */ |
321 | error = VOP_FSYNC(vp, l->l_cred, FSYNC_WAIT, 0, 0); | | 321 | error = VOP_FSYNC(vp, l->l_cred, FSYNC_WAIT, 0, 0); |
322 | if (error) | | 322 | if (error) |
323 | goto out; | | 323 | goto out; |
324 | /* | | 324 | /* |
325 | * Invalidate and free all pages on the snapshot vnode. | | 325 | * Invalidate and free all pages on the snapshot vnode. |
326 | * We will read and write through the buffercache. | | 326 | * We will read and write through the buffercache. |
327 | */ | | 327 | */ |
328 | mutex_enter(&vp->v_interlock); | | 328 | mutex_enter(&vp->v_interlock); |
329 | error = VOP_PUTPAGES(vp, 0, 0, | | 329 | error = VOP_PUTPAGES(vp, 0, 0, |
330 | PGO_ALLPAGES | PGO_CLEANIT | PGO_SYNCIO | PGO_FREE); | | 330 | PGO_ALLPAGES | PGO_CLEANIT | PGO_SYNCIO | PGO_FREE); |
331 | if (error) | | 331 | if (error) |
332 | goto out; | | 332 | goto out; |
333 | /* | | 333 | /* |
334 | * Invalidate short ( < fs_bsize ) buffers. We will always read | | 334 | * Invalidate short ( < fs_bsize ) buffers. We will always read |
335 | * full size buffers later. | | 335 | * full size buffers later. |
336 | */ | | 336 | */ |
337 | mutex_enter(&bufcache_lock); | | 337 | mutex_enter(&bufcache_lock); |
338 | KASSERT(LIST_FIRST(&vp->v_dirtyblkhd) == NULL); | | 338 | KASSERT(LIST_FIRST(&vp->v_dirtyblkhd) == NULL); |
339 | for (bp = LIST_FIRST(&vp->v_cleanblkhd); bp; bp = nbp) { | | 339 | for (bp = LIST_FIRST(&vp->v_cleanblkhd); bp; bp = nbp) { |
340 | nbp = LIST_NEXT(bp, b_vnbufs); | | 340 | nbp = LIST_NEXT(bp, b_vnbufs); |
341 | KASSERT((bp->b_cflags & BC_BUSY) == 0); | | 341 | KASSERT((bp->b_cflags & BC_BUSY) == 0); |
342 | if (bp->b_bcount < fs->fs_bsize) { | | 342 | if (bp->b_bcount < fs->fs_bsize) { |
343 | bp->b_cflags |= BC_BUSY; | | 343 | bp->b_cflags |= BC_BUSY; |
344 | brelsel(bp, BC_INVAL | BC_VFLUSH); | | 344 | brelsel(bp, BC_INVAL | BC_VFLUSH); |
345 | } | | 345 | } |
346 | } | | 346 | } |
347 | mutex_exit(&bufcache_lock); | | 347 | mutex_exit(&bufcache_lock); |
348 | | | 348 | |
349 | out: | | 349 | out: |
350 | if (sbbuf != NULL) { | | 350 | if (sbbuf != NULL) { |
351 | free(copy_fs->fs_csp, M_UFSMNT); | | 351 | free(copy_fs->fs_csp, M_UFSMNT); |
352 | free(sbbuf, M_UFSMNT); | | 352 | free(sbbuf, M_UFSMNT); |
353 | } | | 353 | } |
354 | if (fs->fs_active != NULL) { | | 354 | if (fs->fs_active != NULL) { |
355 | free(fs->fs_active, M_DEVBUF); | | 355 | free(fs->fs_active, M_DEVBUF); |
356 | fs->fs_active = NULL; | | 356 | fs->fs_active = NULL; |
357 | } | | 357 | } |
358 | | | 358 | |
359 | mutex_enter(&si->si_lock); | | 359 | mutex_enter(&si->si_lock); |
360 | if (snaplist != NULL) { | | 360 | if (snaplist != NULL) { |
361 | if (si->si_snapblklist == snaplist) | | 361 | if (si->si_snapblklist == snaplist) |
362 | si->si_snapblklist = NULL; | | 362 | si->si_snapblklist = NULL; |
363 | free(snaplist, M_UFSMNT); | | 363 | free(snaplist, M_UFSMNT); |
364 | } | | 364 | } |
365 | if (error) { | | 365 | if (error) { |
366 | fs->fs_snapinum[snaploc] = 0; | | 366 | fs->fs_snapinum[snaploc] = 0; |
367 | } else { | | 367 | } else { |
368 | /* | | 368 | /* |
369 | * As this is the newest list, it is the most inclusive, so | | 369 | * As this is the newest list, it is the most inclusive, so |
370 | * should replace the previous list. | | 370 | * should replace the previous list. |
371 | */ | | 371 | */ |
372 | si->si_snapblklist = ip->i_snapblklist; | | 372 | si->si_snapblklist = ip->i_snapblklist; |
373 | } | | 373 | } |
374 | si->si_gen++; | | 374 | si->si_gen++; |
375 | mutex_exit(&si->si_lock); | | 375 | mutex_exit(&si->si_lock); |
376 | | | 376 | |
377 | if (snapshot_locked) | | 377 | if (snapshot_locked) |
378 | mutex_exit(&si->si_snaplock); | | 378 | mutex_exit(&si->si_snaplock); |
379 | if (suspended) { | | 379 | if (suspended) { |
380 | vfs_resume(vp->v_mount); | | 380 | vfs_resume(vp->v_mount); |
381 | #ifdef DEBUG | | 381 | #ifdef DEBUG |
382 | getmicrotime(&endtime); | | 382 | getmicrotime(&endtime); |
383 | timersub(&endtime, &starttime, &endtime); | | 383 | timersub(&endtime, &starttime, &endtime); |
384 | printf("%s: suspended %ld.%03ld sec, redo %d of %d\n", | | 384 | printf("%s: suspended %ld.%03ld sec, redo %d of %d\n", |
385 | mp->mnt_stat.f_mntonname, (long)endtime.tv_sec, | | 385 | mp->mnt_stat.f_mntonname, (long)endtime.tv_sec, |
386 | endtime.tv_usec / 1000, redo, fs->fs_ncg); | | 386 | endtime.tv_usec / 1000, redo, fs->fs_ncg); |
387 | #endif | | 387 | #endif |
388 | } | | 388 | } |
389 | if (error) { | | 389 | if (error) { |
390 | if (!UFS_WAPBL_BEGIN(mp)) { | | 390 | if (!UFS_WAPBL_BEGIN(mp)) { |
391 | (void) ffs_truncate(vp, (off_t)0, 0, NOCRED); | | 391 | (void) ffs_truncate(vp, (off_t)0, 0, NOCRED); |
392 | UFS_WAPBL_END(mp); | | 392 | UFS_WAPBL_END(mp); |
393 | } | | 393 | } |
394 | } else | | 394 | } else |
395 | vref(vp); | | 395 | vref(vp); |
396 | return (error); | | 396 | return (error); |
397 | } | | 397 | } |
398 | | | 398 | |
399 | /* | | 399 | /* |
400 | * Prepare vnode to become a snapshot. | | 400 | * Prepare vnode to become a snapshot. |
401 | */ | | 401 | */ |
402 | static int | | 402 | static int |
403 | snapshot_setup(struct mount *mp, struct vnode *vp) | | 403 | snapshot_setup(struct mount *mp, struct vnode *vp) |
404 | { | | 404 | { |
405 | int error, i, len, loc; | | 405 | int error, i, len, loc; |
406 | daddr_t blkno, numblks; | | 406 | daddr_t blkno, numblks; |
407 | struct buf *ibp, *nbp; | | 407 | struct buf *ibp, *nbp; |
408 | struct fs *fs = VFSTOUFS(mp)->um_fs; | | 408 | struct fs *fs = VFSTOUFS(mp)->um_fs; |
409 | struct lwp *l = curlwp; | | 409 | struct lwp *l = curlwp; |
410 | | | 410 | |
411 | /* | | 411 | /* |
412 | * Check mount, exclusive reference and owner. | | 412 | * Check mount, exclusive reference and owner. |
413 | */ | | 413 | */ |
414 | if (vp->v_mount != mp) | | 414 | if (vp->v_mount != mp) |
415 | return EXDEV; | | 415 | return EXDEV; |
416 | if (vp->v_usecount != 1 || vp->v_writecount != 0) | | 416 | if (vp->v_usecount != 1 || vp->v_writecount != 0) |
417 | return EBUSY; | | 417 | return EBUSY; |
418 | if (kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, | | 418 | if (kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, |
419 | NULL) != 0 && | | 419 | NULL) != 0 && |
420 | VTOI(vp)->i_uid != kauth_cred_geteuid(l->l_cred)) | | 420 | VTOI(vp)->i_uid != kauth_cred_geteuid(l->l_cred)) |
421 | return EACCES; | | 421 | return EACCES; |
422 | | | 422 | |
423 | if (vp->v_size != 0) { | | 423 | if (vp->v_size != 0) { |
424 | error = ffs_truncate(vp, 0, 0, NOCRED); | | 424 | error = ffs_truncate(vp, 0, 0, NOCRED); |
425 | if (error) | | 425 | if (error) |
426 | return error; | | 426 | return error; |
427 | } | | 427 | } |
428 | /* | | 428 | /* |
429 | * Write an empty list of preallocated blocks to the end of | | 429 | * Write an empty list of preallocated blocks to the end of |
430 | * the snapshot to set size to at least that of the filesystem. | | 430 | * the snapshot to set size to at least that of the filesystem. |
431 | */ | | 431 | */ |
432 | numblks = howmany(fs->fs_size, fs->fs_frag); | | 432 | numblks = howmany(fs->fs_size, fs->fs_frag); |
433 | blkno = 1; | | 433 | blkno = 1; |
434 | blkno = ufs_rw64(blkno, UFS_FSNEEDSWAP(fs)); | | 434 | blkno = ufs_rw64(blkno, UFS_FSNEEDSWAP(fs)); |
435 | error = vn_rdwr(UIO_WRITE, vp, | | 435 | error = vn_rdwr(UIO_WRITE, vp, |
436 | (void *)&blkno, sizeof(blkno), lblktosize(fs, (off_t)numblks), | | 436 | (void *)&blkno, sizeof(blkno), lblktosize(fs, (off_t)numblks), |
437 | UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, l->l_cred, NULL, NULL); | | 437 | UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, l->l_cred, NULL, NULL); |
438 | if (error) | | 438 | if (error) |
439 | return error; | | 439 | return error; |
440 | /* | | 440 | /* |
441 | * Preallocate critical data structures so that we can copy | | 441 | * Preallocate critical data structures so that we can copy |
442 | * them in without further allocation after we suspend all | | 442 | * them in without further allocation after we suspend all |
443 | * operations on the filesystem. We would like to just release | | 443 | * operations on the filesystem. We would like to just release |
444 | * the allocated buffers without writing them since they will | | 444 | * the allocated buffers without writing them since they will |
445 | * be filled in below once we are ready to go, but this upsets | | 445 | * be filled in below once we are ready to go, but this upsets |
446 | * the soft update code, so we go ahead and write the new buffers. | | 446 | * the soft update code, so we go ahead and write the new buffers. |
447 | * | | 447 | * |
448 | * Allocate all indirect blocks and mark all of them as not | | 448 | * Allocate all indirect blocks and mark all of them as not |
449 | * needing to be copied. | | 449 | * needing to be copied. |
450 | */ | | 450 | */ |
451 | error = UFS_WAPBL_BEGIN(mp); | | 451 | error = UFS_WAPBL_BEGIN(mp); |
452 | if (error) | | 452 | if (error) |
453 | return error; | | 453 | return error; |
454 | for (blkno = NDADDR, i = 0; blkno < numblks; blkno += NINDIR(fs)) { | | 454 | for (blkno = NDADDR, i = 0; blkno < numblks; blkno += NINDIR(fs)) { |
455 | error = ffs_balloc(vp, lblktosize(fs, (off_t)blkno), | | 455 | error = ffs_balloc(vp, lblktosize(fs, (off_t)blkno), |
456 | fs->fs_bsize, l->l_cred, B_METAONLY, &ibp); | | 456 | fs->fs_bsize, l->l_cred, B_METAONLY, &ibp); |
457 | if (error) | | 457 | if (error) |
458 | goto out; | | 458 | goto out; |
459 | if (DOINGSOFTDEP(vp)) | | 459 | if (DOINGSOFTDEP(vp)) |
460 | bawrite(ibp); | | 460 | bawrite(ibp); |
461 | else | | 461 | else |
462 | brelse(ibp, 0); | | 462 | brelse(ibp, 0); |
463 | if ((++i % 16) == 0) { | | 463 | if ((++i % 16) == 0) { |
464 | UFS_WAPBL_END(mp); | | 464 | UFS_WAPBL_END(mp); |
465 | error = UFS_WAPBL_BEGIN(mp); | | 465 | error = UFS_WAPBL_BEGIN(mp); |
466 | if (error) | | 466 | if (error) |
467 | return error; | | 467 | return error; |
468 | } | | 468 | } |
469 | } | | 469 | } |
470 | /* | | 470 | /* |
471 | * Allocate copies for the superblock and its summary information. | | 471 | * Allocate copies for the superblock and its summary information. |
472 | */ | | 472 | */ |
473 | error = ffs_balloc(vp, fs->fs_sblockloc, fs->fs_sbsize, l->l_cred, | | 473 | error = ffs_balloc(vp, fs->fs_sblockloc, fs->fs_sbsize, l->l_cred, |
474 | 0, &nbp); | | 474 | 0, &nbp); |
475 | if (error) | | 475 | if (error) |
476 | goto out; | | 476 | goto out; |
477 | bawrite(nbp); | | 477 | bawrite(nbp); |
478 | blkno = fragstoblks(fs, fs->fs_csaddr); | | 478 | blkno = fragstoblks(fs, fs->fs_csaddr); |
479 | len = howmany(fs->fs_cssize, fs->fs_bsize); | | 479 | len = howmany(fs->fs_cssize, fs->fs_bsize); |
480 | for (loc = 0; loc < len; loc++) { | | 480 | for (loc = 0; loc < len; loc++) { |
481 | error = ffs_balloc(vp, lblktosize(fs, (off_t)(blkno + loc)), | | 481 | error = ffs_balloc(vp, lblktosize(fs, (off_t)(blkno + loc)), |
482 | fs->fs_bsize, l->l_cred, 0, &nbp); | | 482 | fs->fs_bsize, l->l_cred, 0, &nbp); |
483 | if (error) | | 483 | if (error) |
484 | goto out; | | 484 | goto out; |
485 | bawrite(nbp); | | 485 | bawrite(nbp); |
486 | } | | 486 | } |
487 | | | 487 | |
488 | out: | | 488 | out: |
489 | UFS_WAPBL_END(mp); | | 489 | UFS_WAPBL_END(mp); |
490 | return error; | | 490 | return error; |
491 | } | | 491 | } |
492 | | | 492 | |
493 | /* | | 493 | /* |
494 | * Create a copy of the superblock and its summary information. | | 494 | * Create a copy of the superblock and its summary information. |
495 | * It is up to the caller to free copyfs and copy_fs->fs_csp. | | 495 | * It is up to the caller to free copyfs and copy_fs->fs_csp. |
496 | */ | | 496 | */ |
497 | static int | | 497 | static int |
498 | snapshot_copyfs(struct mount *mp, struct vnode *vp, void **sbbuf) | | 498 | snapshot_copyfs(struct mount *mp, struct vnode *vp, void **sbbuf) |
499 | { | | 499 | { |
500 | int error, i, len, loc, size; | | 500 | int error, i, len, loc, size; |
501 | void *space; | | 501 | void *space; |
502 | int32_t *lp; | | 502 | int32_t *lp; |
503 | struct buf *bp; | | 503 | struct buf *bp; |
504 | struct fs *copyfs, *fs = VFSTOUFS(mp)->um_fs; | | 504 | struct fs *copyfs, *fs = VFSTOUFS(mp)->um_fs; |
505 | struct lwp *l = curlwp; | | 505 | struct lwp *l = curlwp; |
506 | struct vnode *devvp = VTOI(vp)->i_devvp; | | 506 | struct vnode *devvp = VTOI(vp)->i_devvp; |
507 | | | 507 | |
508 | /* | | 508 | /* |
509 | * Grab a copy of the superblock and its summary information. | | 509 | * Grab a copy of the superblock and its summary information. |
510 | * We delay writing it until the suspension is released below. | | 510 | * We delay writing it until the suspension is released below. |
511 | */ | | 511 | */ |
512 | *sbbuf = malloc(fs->fs_bsize, M_UFSMNT, M_WAITOK); | | 512 | *sbbuf = malloc(fs->fs_bsize, M_UFSMNT, M_WAITOK); |
513 | loc = blkoff(fs, fs->fs_sblockloc); | | 513 | loc = blkoff(fs, fs->fs_sblockloc); |
514 | if (loc > 0) | | 514 | if (loc > 0) |
515 | memset(*sbbuf, 0, loc); | | 515 | memset(*sbbuf, 0, loc); |
516 | copyfs = (struct fs *)((char *)(*sbbuf) + loc); | | 516 | copyfs = (struct fs *)((char *)(*sbbuf) + loc); |
517 | bcopy(fs, copyfs, fs->fs_sbsize); | | 517 | bcopy(fs, copyfs, fs->fs_sbsize); |
518 | size = fs->fs_bsize < SBLOCKSIZE ? fs->fs_bsize : SBLOCKSIZE; | | 518 | size = fs->fs_bsize < SBLOCKSIZE ? fs->fs_bsize : SBLOCKSIZE; |
519 | if (fs->fs_sbsize < size) | | 519 | if (fs->fs_sbsize < size) |
520 | memset((char *)(*sbbuf) + loc + fs->fs_sbsize, 0, | | 520 | memset((char *)(*sbbuf) + loc + fs->fs_sbsize, 0, |
521 | size - fs->fs_sbsize); | | 521 | size - fs->fs_sbsize); |
522 | size = blkroundup(fs, fs->fs_cssize); | | 522 | size = blkroundup(fs, fs->fs_cssize); |
523 | if (fs->fs_contigsumsize > 0) | | 523 | if (fs->fs_contigsumsize > 0) |
524 | size += fs->fs_ncg * sizeof(int32_t); | | 524 | size += fs->fs_ncg * sizeof(int32_t); |
525 | space = malloc(size, M_UFSMNT, M_WAITOK); | | 525 | space = malloc(size, M_UFSMNT, M_WAITOK); |
526 | copyfs->fs_csp = space; | | 526 | copyfs->fs_csp = space; |
527 | bcopy(fs->fs_csp, copyfs->fs_csp, fs->fs_cssize); | | 527 | bcopy(fs->fs_csp, copyfs->fs_csp, fs->fs_cssize); |
528 | space = (char *)space + fs->fs_cssize; | | 528 | space = (char *)space + fs->fs_cssize; |
529 | loc = howmany(fs->fs_cssize, fs->fs_fsize); | | 529 | loc = howmany(fs->fs_cssize, fs->fs_fsize); |
530 | i = fs->fs_frag - loc % fs->fs_frag; | | 530 | i = fs->fs_frag - loc % fs->fs_frag; |
531 | len = (i == fs->fs_frag) ? 0 : i * fs->fs_fsize; | | 531 | len = (i == fs->fs_frag) ? 0 : i * fs->fs_fsize; |
532 | if (len > 0) { | | 532 | if (len > 0) { |
533 | if ((error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + loc), | | 533 | if ((error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + loc), |
534 | len, l->l_cred, 0, &bp)) != 0) { | | 534 | len, l->l_cred, 0, &bp)) != 0) { |
535 | brelse(bp, 0); | | 535 | brelse(bp, 0); |
536 | free(copyfs->fs_csp, M_UFSMNT); | | 536 | free(copyfs->fs_csp, M_UFSMNT); |
537 | free(*sbbuf, M_UFSMNT); | | 537 | free(*sbbuf, M_UFSMNT); |
538 | *sbbuf = NULL; | | 538 | *sbbuf = NULL; |
539 | return error; | | 539 | return error; |
540 | } | | 540 | } |
541 | bcopy(bp->b_data, space, (u_int)len); | | 541 | bcopy(bp->b_data, space, (u_int)len); |
542 | space = (char *)space + len; | | 542 | space = (char *)space + len; |
543 | brelse(bp, BC_INVAL | BC_NOCACHE); | | 543 | brelse(bp, BC_INVAL | BC_NOCACHE); |
544 | } | | 544 | } |
545 | if (fs->fs_contigsumsize > 0) { | | 545 | if (fs->fs_contigsumsize > 0) { |
546 | copyfs->fs_maxcluster = lp = space; | | 546 | copyfs->fs_maxcluster = lp = space; |
547 | for (i = 0; i < fs->fs_ncg; i++) | | 547 | for (i = 0; i < fs->fs_ncg; i++) |
548 | *lp++ = fs->fs_contigsumsize; | | 548 | *lp++ = fs->fs_contigsumsize; |
549 | } | | 549 | } |
550 | if (mp->mnt_wapbl) | | 550 | if (mp->mnt_wapbl) |
551 | copyfs->fs_flags &= ~FS_DOWAPBL; | | 551 | copyfs->fs_flags &= ~FS_DOWAPBL; |
552 | return 0; | | 552 | return 0; |
553 | } | | 553 | } |
554 | | | 554 | |
555 | /* | | 555 | /* |
556 | * We must check for active files that have been unlinked (e.g., with a zero | | 556 | * We must check for active files that have been unlinked (e.g., with a zero |
557 | * link count). We have to expunge all trace of these files from the snapshot | | 557 | * link count). We have to expunge all trace of these files from the snapshot |
558 | * so that they are not reclaimed prematurely by fsck or unnecessarily dumped. | | 558 | * so that they are not reclaimed prematurely by fsck or unnecessarily dumped. |
559 | * Note that we skip unlinked snapshot files as they will be handled separately. | | 559 | * Note that we skip unlinked snapshot files as they will be handled separately. |
560 | * Calculate the snapshot list size and create a preliminary list. | | 560 | * Calculate the snapshot list size and create a preliminary list. |
561 | */ | | 561 | */ |
562 | static int | | 562 | static int |
563 | snapshot_expunge(struct mount *mp, struct vnode *vp, struct fs *copy_fs, | | 563 | snapshot_expunge(struct mount *mp, struct vnode *vp, struct fs *copy_fs, |
564 | daddr_t *snaplistsize, daddr_t **snaplist) | | 564 | daddr_t *snaplistsize, daddr_t **snaplist) |
565 | { | | 565 | { |
566 | bool has_wapbl = false; | | 566 | bool has_wapbl = false; |
567 | int cg, error, len, loc; | | 567 | int cg, error, len, loc; |
568 | daddr_t blkno, *blkp; | | 568 | daddr_t blkno, *blkp; |
569 | struct fs *fs = VFSTOUFS(mp)->um_fs; | | 569 | struct fs *fs = VFSTOUFS(mp)->um_fs; |
570 | struct inode *xp; | | 570 | struct inode *xp; |
571 | struct lwp *l = curlwp; | | 571 | struct lwp *l = curlwp; |
572 | struct vattr vat; | | 572 | struct vattr vat; |
573 | struct vnode *logvp = NULL, *mvp = NULL, *xvp; | | 573 | struct vnode *logvp = NULL, *mvp = NULL, *xvp; |
574 | | | 574 | |
575 | *snaplist = NULL; | | 575 | *snaplist = NULL; |
576 | /* | | 576 | /* |
577 | * Get the log inode if any. | | 577 | * Get the log inode if any. |
578 | */ | | 578 | */ |
579 | if ((fs->fs_flags & FS_DOWAPBL) && | | 579 | if ((fs->fs_flags & FS_DOWAPBL) && |
580 | fs->fs_journal_location == UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM) { | | 580 | fs->fs_journal_location == UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM) { |
581 | error = VFS_VGET(mp, | | 581 | error = VFS_VGET(mp, |
582 | fs->fs_journallocs[UFS_WAPBL_INFS_INO], &logvp); | | 582 | fs->fs_journallocs[UFS_WAPBL_INFS_INO], &logvp); |
583 | if (error) | | 583 | if (error) |
584 | goto out; | | 584 | goto out; |
585 | } | | 585 | } |
586 | /* | | 586 | /* |
587 | * Allocate a marker vnode. | | 587 | * Allocate a marker vnode. |
588 | */ | | 588 | */ |
589 | if ((mvp = vnalloc(mp)) == NULL) { | | 589 | if ((mvp = vnalloc(mp)) == NULL) { |
590 | error = ENOMEM; | | 590 | error = ENOMEM; |
591 | goto out; | | 591 | goto out; |
592 | } | | 592 | } |
593 | /* | | 593 | /* |
594 | * We also calculate the needed size for the snapshot list. | | 594 | * We also calculate the needed size for the snapshot list. |
595 | */ | | 595 | */ |
596 | *snaplistsize = fs->fs_ncg + howmany(fs->fs_cssize, fs->fs_bsize) + | | 596 | *snaplistsize = fs->fs_ncg + howmany(fs->fs_cssize, fs->fs_bsize) + |
597 | FSMAXSNAP + 1 /* superblock */ + 1 /* last block */ + 1 /* size */; | | 597 | FSMAXSNAP + 1 /* superblock */ + 1 /* last block */ + 1 /* size */; |
598 | error = UFS_WAPBL_BEGIN(mp); | | 598 | error = UFS_WAPBL_BEGIN(mp); |
599 | if (error) | | 599 | if (error) |
600 | goto out; | | 600 | goto out; |
601 | has_wapbl = true; | | 601 | has_wapbl = true; |
602 | mutex_enter(&mntvnode_lock); | | 602 | mutex_enter(&mntvnode_lock); |
603 | /* | | 603 | /* |
604 | * NOTE: not using the TAILQ_FOREACH here since in this loop vgone() | | 604 | * NOTE: not using the TAILQ_FOREACH here since in this loop vgone() |
605 | * and vclean() can be called indirectly | | 605 | * and vclean() can be called indirectly |
606 | */ | | 606 | */ |
607 | for (xvp = TAILQ_FIRST(&mp->mnt_vnodelist); xvp; xvp = vunmark(mvp)) { | | 607 | for (xvp = TAILQ_FIRST(&mp->mnt_vnodelist); xvp; xvp = vunmark(mvp)) { |
608 | vmark(mvp, xvp); | | 608 | vmark(mvp, xvp); |
609 | /* | | 609 | /* |
610 | * Make sure this vnode wasn't reclaimed in getnewvnode(). | | 610 | * Make sure this vnode wasn't reclaimed in getnewvnode(). |
611 | * Start over if it has (it won't be on the list anymore). | | 611 | * Start over if it has (it won't be on the list anymore). |
612 | */ | | 612 | */ |
613 | if (xvp->v_mount != mp || vismarker(xvp)) | | 613 | if (xvp->v_mount != mp || vismarker(xvp)) |
614 | continue; | | 614 | continue; |
615 | mutex_enter(&xvp->v_interlock); | | 615 | mutex_enter(&xvp->v_interlock); |
616 | if ((xvp->v_iflag & VI_XLOCK) || | | 616 | if ((xvp->v_iflag & VI_XLOCK) || |
617 | xvp->v_usecount == 0 || xvp->v_type == VNON || | | 617 | xvp->v_usecount == 0 || xvp->v_type == VNON || |
618 | VTOI(xvp) == NULL || | | 618 | VTOI(xvp) == NULL || |
619 | (VTOI(xvp)->i_flags & SF_SNAPSHOT)) { | | 619 | (VTOI(xvp)->i_flags & SF_SNAPSHOT)) { |
620 | mutex_exit(&xvp->v_interlock); | | 620 | mutex_exit(&xvp->v_interlock); |
621 | continue; | | 621 | continue; |
622 | } | | 622 | } |
623 | mutex_exit(&mntvnode_lock); | | 623 | mutex_exit(&mntvnode_lock); |
624 | /* | | 624 | /* |
625 | * XXXAD should increase vnode ref count to prevent it | | 625 | * XXXAD should increase vnode ref count to prevent it |
626 | * disappearing or being recycled. | | 626 | * disappearing or being recycled. |
627 | */ | | 627 | */ |
628 | mutex_exit(&xvp->v_interlock); | | 628 | mutex_exit(&xvp->v_interlock); |
629 | #ifdef DEBUG | | 629 | #ifdef DEBUG |
630 | if (snapdebug) | | 630 | if (snapdebug) |
631 | vprint("ffs_snapshot: busy vnode", xvp); | | 631 | vprint("ffs_snapshot: busy vnode", xvp); |
632 | #endif | | 632 | #endif |
633 | xp = VTOI(xvp); | | 633 | xp = VTOI(xvp); |
634 | if (xvp != logvp) { | | 634 | if (xvp != logvp) { |
635 | if (VOP_GETATTR(xvp, &vat, l->l_cred) == 0 && | | 635 | if (VOP_GETATTR(xvp, &vat, l->l_cred) == 0 && |
636 | vat.va_nlink > 0) { | | 636 | vat.va_nlink > 0) { |
637 | mutex_enter(&mntvnode_lock); | | 637 | mutex_enter(&mntvnode_lock); |
638 | continue; | | 638 | continue; |
639 | } | | 639 | } |
640 | if (ffs_checkfreefile(copy_fs, vp, xp->i_number)) { | | 640 | if (ffs_checkfreefile(copy_fs, vp, xp->i_number)) { |
641 | mutex_enter(&mntvnode_lock); | | 641 | mutex_enter(&mntvnode_lock); |
642 | continue; | | 642 | continue; |
643 | } | | 643 | } |
644 | } | | 644 | } |
645 | /* | | 645 | /* |
646 | * If there is a fragment, clear it here. | | 646 | * If there is a fragment, clear it here. |
647 | */ | | 647 | */ |
648 | blkno = 0; | | 648 | blkno = 0; |
649 | loc = howmany(xp->i_size, fs->fs_bsize) - 1; | | 649 | loc = howmany(xp->i_size, fs->fs_bsize) - 1; |
650 | if (loc < NDADDR) { | | 650 | if (loc < NDADDR) { |
651 | len = fragroundup(fs, blkoff(fs, xp->i_size)); | | 651 | len = fragroundup(fs, blkoff(fs, xp->i_size)); |
652 | if (len > 0 && len < fs->fs_bsize) { | | 652 | if (len > 0 && len < fs->fs_bsize) { |
653 | ffs_blkfree(copy_fs, vp, db_get(xp, loc), | | 653 | ffs_blkfree(copy_fs, vp, db_get(xp, loc), |
654 | len, xp->i_number); | | 654 | len, xp->i_number); |
655 | blkno = db_get(xp, loc); | | 655 | blkno = db_get(xp, loc); |
656 | db_assign(xp, loc, 0); | | 656 | db_assign(xp, loc, 0); |
657 | } | | 657 | } |
658 | } | | 658 | } |
659 | *snaplistsize += 1; | | 659 | *snaplistsize += 1; |
660 | error = expunge(vp, xp, copy_fs, fullacct, BLK_NOCOPY); | | 660 | error = expunge(vp, xp, copy_fs, fullacct, BLK_NOCOPY); |
661 | if (blkno) | | 661 | if (blkno) |
662 | db_assign(xp, loc, blkno); | | 662 | db_assign(xp, loc, blkno); |
663 | if (!error) | | 663 | if (!error) |
664 | error = ffs_freefile(copy_fs, vp, xp->i_number, | | 664 | error = ffs_freefile(copy_fs, vp, xp->i_number, |
665 | xp->i_mode); | | 665 | xp->i_mode); |
666 | if (error) { | | 666 | if (error) { |
667 | (void)vunmark(mvp); | | 667 | (void)vunmark(mvp); |
668 | goto out; | | 668 | goto out; |
669 | } | | 669 | } |
670 | mutex_enter(&mntvnode_lock); | | 670 | mutex_enter(&mntvnode_lock); |
671 | } | | 671 | } |
672 | mutex_exit(&mntvnode_lock); | | 672 | mutex_exit(&mntvnode_lock); |
673 | /* | | 673 | /* |
674 | * Create a preliminary list of preallocated snapshot blocks. | | 674 | * Create a preliminary list of preallocated snapshot blocks. |
675 | */ | | 675 | */ |
676 | *snaplist = malloc(*snaplistsize * sizeof(daddr_t), M_UFSMNT, M_WAITOK); | | 676 | *snaplist = malloc(*snaplistsize * sizeof(daddr_t), M_UFSMNT, M_WAITOK); |
677 | blkp = &(*snaplist)[1]; | | 677 | blkp = &(*snaplist)[1]; |
678 | *blkp++ = lblkno(fs, fs->fs_sblockloc); | | 678 | *blkp++ = lblkno(fs, fs->fs_sblockloc); |
679 | blkno = fragstoblks(fs, fs->fs_csaddr); | | 679 | blkno = fragstoblks(fs, fs->fs_csaddr); |
680 | for (cg = 0; cg < fs->fs_ncg; cg++) { | | 680 | for (cg = 0; cg < fs->fs_ncg; cg++) { |
681 | if (fragstoblks(fs, cgtod(fs, cg)) > blkno) | | 681 | if (fragstoblks(fs, cgtod(fs, cg)) > blkno) |
682 | break; | | 682 | break; |
683 | *blkp++ = fragstoblks(fs, cgtod(fs, cg)); | | 683 | *blkp++ = fragstoblks(fs, cgtod(fs, cg)); |
684 | } | | 684 | } |
685 | len = howmany(fs->fs_cssize, fs->fs_bsize); | | 685 | len = howmany(fs->fs_cssize, fs->fs_bsize); |
686 | for (loc = 0; loc < len; loc++) | | 686 | for (loc = 0; loc < len; loc++) |
687 | *blkp++ = blkno + loc; | | 687 | *blkp++ = blkno + loc; |
688 | for (; cg < fs->fs_ncg; cg++) | | 688 | for (; cg < fs->fs_ncg; cg++) |
689 | *blkp++ = fragstoblks(fs, cgtod(fs, cg)); | | 689 | *blkp++ = fragstoblks(fs, cgtod(fs, cg)); |
690 | | | 690 | |
691 | out: | | 691 | out: |
692 | if (has_wapbl) | | 692 | if (has_wapbl) |
693 | UFS_WAPBL_END(mp); | | 693 | UFS_WAPBL_END(mp); |
694 | if (mvp != NULL) | | 694 | if (mvp != NULL) |
695 | vnfree(mvp); | | 695 | vnfree(mvp); |
696 | if (logvp != NULL) | | 696 | if (logvp != NULL) |
697 | vput(logvp); | | 697 | vput(logvp); |
698 | if (error && *snaplist != NULL) { | | 698 | if (error && *snaplist != NULL) { |
699 | free(*snaplist, M_UFSMNT); | | 699 | free(*snaplist, M_UFSMNT); |
700 | *snaplist = NULL; | | 700 | *snaplist = NULL; |
701 | } | | 701 | } |
702 | | | 702 | |
703 | return error; | | 703 | return error; |
704 | } | | 704 | } |
705 | | | 705 | |
706 | /* | | 706 | /* |
707 | * Copy allocation information from all the snapshots in this snapshot and | | 707 | * Copy allocation information from all the snapshots in this snapshot and |
708 | * then expunge them from its view. Also, collect the list of allocated | | 708 | * then expunge them from its view. Also, collect the list of allocated |
709 | * blocks in i_snapblklist. | | 709 | * blocks in i_snapblklist. |
710 | */ | | 710 | */ |
711 | static int | | 711 | static int |
712 | snapshot_expunge_snap(struct mount *mp, struct vnode *vp, | | 712 | snapshot_expunge_snap(struct mount *mp, struct vnode *vp, |
713 | struct fs *copy_fs, daddr_t snaplistsize) | | 713 | struct fs *copy_fs, daddr_t snaplistsize) |
714 | { | | 714 | { |
715 | int error, i; | | 715 | int error, i; |
716 | daddr_t numblks, *snaplist = NULL; | | 716 | daddr_t numblks, *snaplist = NULL; |
717 | struct fs *fs = VFSTOUFS(mp)->um_fs; | | 717 | struct fs *fs = VFSTOUFS(mp)->um_fs; |
718 | struct inode *ip = VTOI(vp), *xp; | | 718 | struct inode *ip = VTOI(vp), *xp; |
719 | struct lwp *l = curlwp; | | 719 | struct lwp *l = curlwp; |
720 | struct snap_info *si = VFSTOUFS(mp)->um_snapinfo; | | 720 | struct snap_info *si = VFSTOUFS(mp)->um_snapinfo; |
721 | | | 721 | |
722 | error = UFS_WAPBL_BEGIN(mp); | | 722 | error = UFS_WAPBL_BEGIN(mp); |
723 | if (error) | | 723 | if (error) |
724 | return error; | | 724 | return error; |
725 | TAILQ_FOREACH(xp, &si->si_snapshots, i_nextsnap) { | | 725 | TAILQ_FOREACH(xp, &si->si_snapshots, i_nextsnap) { |
726 | if (xp == ip) | | 726 | if (xp == ip) |
727 | break; | | 727 | break; |
728 | error = expunge(vp, xp, fs, snapacct, BLK_SNAP); | | 728 | error = expunge(vp, xp, fs, snapacct, BLK_SNAP); |
729 | if (error) | | 729 | if (error) |
730 | break; | | 730 | break; |
731 | if (xp->i_ffs_effnlink != 0) | | 731 | if (xp->i_ffs_effnlink != 0) |
732 | continue; | | 732 | continue; |
733 | error = ffs_freefile(copy_fs, vp, xp->i_number, xp->i_mode); | | 733 | error = ffs_freefile(copy_fs, vp, xp->i_number, xp->i_mode); |
734 | if (error) | | 734 | if (error) |
735 | break; | | 735 | break; |
736 | } | | 736 | } |
737 | if (error) | | 737 | if (error) |
738 | goto out; | | 738 | goto out; |
739 | /* | | 739 | /* |
740 | * Allocate space for the full list of preallocated snapshot blocks. | | 740 | * Allocate space for the full list of preallocated snapshot blocks. |
741 | */ | | 741 | */ |
742 | snaplist = malloc(snaplistsize * sizeof(daddr_t), M_UFSMNT, M_WAITOK); | | 742 | snaplist = malloc(snaplistsize * sizeof(daddr_t), M_UFSMNT, M_WAITOK); |
743 | ip->i_snapblklist = &snaplist[1]; | | 743 | ip->i_snapblklist = &snaplist[1]; |
744 | /* | | 744 | /* |
745 | * Expunge the blocks used by the snapshots from the set of | | 745 | * Expunge the blocks used by the snapshots from the set of |
746 | * blocks marked as used in the snapshot bitmaps. Also, collect | | 746 | * blocks marked as used in the snapshot bitmaps. Also, collect |
747 | * the list of allocated blocks in i_snapblklist. | | 747 | * the list of allocated blocks in i_snapblklist. |
748 | */ | | 748 | */ |
749 | error = expunge(vp, ip, copy_fs, mapacct, BLK_SNAP); | | 749 | error = expunge(vp, ip, copy_fs, mapacct, BLK_SNAP); |
750 | if (error) | | 750 | if (error) |
751 | goto out; | | 751 | goto out; |
752 | if (snaplistsize < ip->i_snapblklist - snaplist) | | 752 | if (snaplistsize < ip->i_snapblklist - snaplist) |
753 | panic("ffs_snapshot: list too small"); | | 753 | panic("ffs_snapshot: list too small"); |
754 | snaplistsize = ip->i_snapblklist - snaplist; | | 754 | snaplistsize = ip->i_snapblklist - snaplist; |
755 | snaplist[0] = snaplistsize; | | 755 | snaplist[0] = snaplistsize; |
756 | ip->i_snapblklist = &snaplist[0]; | | 756 | ip->i_snapblklist = &snaplist[0]; |
757 | /* | | 757 | /* |
758 | * Write out the list of allocated blocks to the end of the snapshot. | | 758 | * Write out the list of allocated blocks to the end of the snapshot. |
759 | */ | | 759 | */ |
760 | numblks = howmany(fs->fs_size, fs->fs_frag); | | 760 | numblks = howmany(fs->fs_size, fs->fs_frag); |
761 | for (i = 0; i < snaplistsize; i++) | | 761 | for (i = 0; i < snaplistsize; i++) |
762 | snaplist[i] = ufs_rw64(snaplist[i], UFS_FSNEEDSWAP(fs)); | | 762 | snaplist[i] = ufs_rw64(snaplist[i], UFS_FSNEEDSWAP(fs)); |
763 | error = vn_rdwr(UIO_WRITE, vp, (void *)snaplist, | | 763 | error = vn_rdwr(UIO_WRITE, vp, (void *)snaplist, |
764 | snaplistsize * sizeof(daddr_t), lblktosize(fs, (off_t)numblks), | | 764 | snaplistsize * sizeof(daddr_t), lblktosize(fs, (off_t)numblks), |
765 | UIO_SYSSPACE, IO_NODELOCKED | IO_JOURNALLOCKED | IO_UNIT, | | 765 | UIO_SYSSPACE, IO_NODELOCKED | IO_JOURNALLOCKED | IO_UNIT, |
766 | l->l_cred, NULL, NULL); | | 766 | l->l_cred, NULL, NULL); |
767 | for (i = 0; i < snaplistsize; i++) | | 767 | for (i = 0; i < snaplistsize; i++) |
768 | snaplist[i] = ufs_rw64(snaplist[i], UFS_FSNEEDSWAP(fs)); | | 768 | snaplist[i] = ufs_rw64(snaplist[i], UFS_FSNEEDSWAP(fs)); |
769 | out: | | 769 | out: |
770 | UFS_WAPBL_END(mp); | | 770 | UFS_WAPBL_END(mp); |
771 | if (error && snaplist != NULL) { | | 771 | if (error && snaplist != NULL) { |
772 | free(snaplist, M_UFSMNT); | | 772 | free(snaplist, M_UFSMNT); |
773 | ip->i_snapblklist = NULL; | | 773 | ip->i_snapblklist = NULL; |
774 | } | | 774 | } |
775 | return error; | | 775 | return error; |
776 | } | | 776 | } |
777 | | | 777 | |
778 | /* | | 778 | /* |
779 | * Write the superblock and its summary information to the snapshot. | | 779 | * Write the superblock and its summary information to the snapshot. |
780 | * Make sure, the first NDADDR blocks get copied to the snapshot. | | 780 | * Make sure, the first NDADDR blocks get copied to the snapshot. |
781 | */ | | 781 | */ |
782 | static int | | 782 | static int |
783 | snapshot_writefs(struct mount *mp, struct vnode *vp, void *sbbuf) | | 783 | snapshot_writefs(struct mount *mp, struct vnode *vp, void *sbbuf) |
784 | { | | 784 | { |
785 | int error, len, loc; | | 785 | int error, len, loc; |
786 | void *space; | | 786 | void *space; |
787 | daddr_t blkno; | | 787 | daddr_t blkno; |
788 | struct buf *bp; | | 788 | struct buf *bp; |
789 | struct fs *copyfs, *fs = VFSTOUFS(mp)->um_fs; | | 789 | struct fs *copyfs, *fs = VFSTOUFS(mp)->um_fs; |
790 | struct inode *ip = VTOI(vp); | | 790 | struct inode *ip = VTOI(vp); |
791 | struct lwp *l = curlwp; | | 791 | struct lwp *l = curlwp; |
792 | | | 792 | |
793 | copyfs = (struct fs *)((char *)sbbuf + blkoff(fs, fs->fs_sblockloc)); | | 793 | copyfs = (struct fs *)((char *)sbbuf + blkoff(fs, fs->fs_sblockloc)); |
794 | | | 794 | |
795 | /* | | 795 | /* |
796 | * Write the superblock and its summary information | | 796 | * Write the superblock and its summary information |
797 | * to the snapshot. | | 797 | * to the snapshot. |
798 | */ | | 798 | */ |
799 | blkno = fragstoblks(fs, fs->fs_csaddr); | | 799 | blkno = fragstoblks(fs, fs->fs_csaddr); |
800 | len = howmany(fs->fs_cssize, fs->fs_bsize); | | 800 | len = howmany(fs->fs_cssize, fs->fs_bsize); |
801 | space = copyfs->fs_csp; | | 801 | space = copyfs->fs_csp; |
802 | #ifdef FFS_EI | | 802 | #ifdef FFS_EI |
803 | if (UFS_FSNEEDSWAP(fs)) { | | 803 | if (UFS_FSNEEDSWAP(fs)) { |
804 | ffs_sb_swap(copyfs, copyfs); | | 804 | ffs_sb_swap(copyfs, copyfs); |
805 | ffs_csum_swap(space, space, fs->fs_cssize); | | 805 | ffs_csum_swap(space, space, fs->fs_cssize); |
806 | } | | 806 | } |
807 | #endif | | 807 | #endif |
808 | error = UFS_WAPBL_BEGIN(mp); | | 808 | error = UFS_WAPBL_BEGIN(mp); |
809 | if (error) | | 809 | if (error) |
810 | return error; | | 810 | return error; |
811 | for (loc = 0; loc < len; loc++) { | | 811 | for (loc = 0; loc < len; loc++) { |
812 | error = bread(vp, blkno + loc, fs->fs_bsize, l->l_cred, | | 812 | error = bread(vp, blkno + loc, fs->fs_bsize, l->l_cred, |
813 | B_MODIFY, &bp); | | 813 | B_MODIFY, &bp); |
814 | if (error) { | | 814 | if (error) { |
815 | brelse(bp, 0); | | 815 | brelse(bp, 0); |
816 | break; | | 816 | break; |
817 | } | | 817 | } |
818 | bcopy(space, bp->b_data, fs->fs_bsize); | | 818 | bcopy(space, bp->b_data, fs->fs_bsize); |
819 | space = (char *)space + fs->fs_bsize; | | 819 | space = (char *)space + fs->fs_bsize; |
820 | bawrite(bp); | | 820 | bawrite(bp); |
821 | } | | 821 | } |
822 | if (error) | | 822 | if (error) |
823 | goto out; | | 823 | goto out; |
824 | error = bread(vp, lblkno(fs, fs->fs_sblockloc), | | 824 | error = bread(vp, lblkno(fs, fs->fs_sblockloc), |
825 | fs->fs_bsize, l->l_cred, B_MODIFY, &bp); | | 825 | fs->fs_bsize, l->l_cred, B_MODIFY, &bp); |
826 | if (error) { | | 826 | if (error) { |
827 | brelse(bp, 0); | | 827 | brelse(bp, 0); |
828 | goto out; | | 828 | goto out; |
829 | } else { | | 829 | } else { |
830 | bcopy(sbbuf, bp->b_data, fs->fs_bsize); | | 830 | bcopy(sbbuf, bp->b_data, fs->fs_bsize); |
831 | bawrite(bp); | | 831 | bawrite(bp); |
832 | } | | 832 | } |
833 | /* | | 833 | /* |
834 | * Copy the first NDADDR blocks to the snapshot so ffs_copyonwrite() | | 834 | * Copy the first NDADDR blocks to the snapshot so ffs_copyonwrite() |
835 | * and ffs_snapblkfree() will always work on indirect blocks. | | 835 | * and ffs_snapblkfree() will always work on indirect blocks. |
836 | */ | | 836 | */ |
837 | for (loc = 0; loc < NDADDR; loc++) { | | 837 | for (loc = 0; loc < NDADDR; loc++) { |
838 | if (db_get(ip, loc) != 0) | | 838 | if (db_get(ip, loc) != 0) |
839 | continue; | | 839 | continue; |
840 | error = ffs_balloc(vp, lblktosize(fs, (off_t)loc), | | 840 | error = ffs_balloc(vp, lblktosize(fs, (off_t)loc), |
841 | fs->fs_bsize, l->l_cred, 0, &bp); | | 841 | fs->fs_bsize, l->l_cred, 0, &bp); |
842 | if (error) | | 842 | if (error) |
843 | break; | | 843 | break; |
844 | error = rwfsblk(vp, B_READ, bp->b_data, loc); | | 844 | error = rwfsblk(vp, B_READ, bp->b_data, loc); |
845 | if (error) { | | 845 | if (error) { |
846 | brelse(bp, 0); | | 846 | brelse(bp, 0); |
847 | break; | | 847 | break; |
848 | } | | 848 | } |
849 | bawrite(bp); | | 849 | bawrite(bp); |
850 | } | | 850 | } |
851 | | | 851 | |
852 | out: | | 852 | out: |
853 | UFS_WAPBL_END(mp); | | 853 | UFS_WAPBL_END(mp); |
854 | return error; | | 854 | return error; |
855 | } | | 855 | } |
856 | | | 856 | |
857 | /* | | 857 | /* |
858 | * Copy all cylinder group maps. | | 858 | * Copy all cylinder group maps. |
859 | */ | | 859 | */ |
860 | static int | | 860 | static int |
861 | cgaccount(struct vnode *vp, int passno, int *redo) | | 861 | cgaccount(struct vnode *vp, int passno, int *redo) |
862 | { | | 862 | { |
863 | int cg, error; | | 863 | int cg, error; |
864 | struct buf *nbp; | | 864 | struct buf *nbp; |
865 | struct fs *fs = VTOI(vp)->i_fs; | | 865 | struct fs *fs = VTOI(vp)->i_fs; |
866 | | | 866 | |
867 | error = UFS_WAPBL_BEGIN(vp->v_mount); | | 867 | error = UFS_WAPBL_BEGIN(vp->v_mount); |
868 | if (error) | | 868 | if (error) |
869 | return error; | | 869 | return error; |
870 | if (redo != NULL) | | 870 | if (redo != NULL) |
871 | *redo = 0; | | 871 | *redo = 0; |
872 | if (passno == 1) | | 872 | if (passno == 1) |
873 | fs->fs_active = malloc(howmany(fs->fs_ncg, NBBY), | | 873 | fs->fs_active = malloc(howmany(fs->fs_ncg, NBBY), |
874 | M_DEVBUF, M_WAITOK | M_ZERO); | | 874 | M_DEVBUF, M_WAITOK | M_ZERO); |
875 | for (cg = 0; cg < fs->fs_ncg; cg++) { | | 875 | for (cg = 0; cg < fs->fs_ncg; cg++) { |
876 | if (passno == 2 && ACTIVECG_ISSET(fs, cg)) | | 876 | if (passno == 2 && ACTIVECG_ISSET(fs, cg)) |
877 | continue; | | 877 | continue; |
878 | if (redo != NULL) | | 878 | if (redo != NULL) |
879 | *redo += 1; | | 879 | *redo += 1; |
880 | error = ffs_balloc(vp, lfragtosize(fs, cgtod(fs, cg)), | | 880 | error = ffs_balloc(vp, lfragtosize(fs, cgtod(fs, cg)), |
881 | fs->fs_bsize, curlwp->l_cred, 0, &nbp); | | 881 | fs->fs_bsize, curlwp->l_cred, 0, &nbp); |
882 | if (error) | | 882 | if (error) |
883 | break; | | 883 | break; |
884 | error = cgaccount1(cg, vp, nbp->b_data, passno); | | 884 | error = cgaccount1(cg, vp, nbp->b_data, passno); |
885 | bawrite(nbp); | | 885 | bawrite(nbp); |
886 | if (error) | | 886 | if (error) |
887 | break; | | 887 | break; |
888 | } | | 888 | } |
889 | UFS_WAPBL_END(vp->v_mount); | | 889 | UFS_WAPBL_END(vp->v_mount); |
890 | return error; | | 890 | return error; |
891 | } | | 891 | } |
892 | | | 892 | |
893 | /* | | 893 | /* |
894 | * Copy a cylinder group map. All the unallocated blocks are marked | | 894 | * Copy a cylinder group map. All the unallocated blocks are marked |
895 | * BLK_NOCOPY so that the snapshot knows that it need not copy them | | 895 | * BLK_NOCOPY so that the snapshot knows that it need not copy them |
896 | * if they are later written. If passno is one, then this is a first | | 896 | * if they are later written. If passno is one, then this is a first |
897 | * pass, so only setting needs to be done. If passno is 2, then this | | 897 | * pass, so only setting needs to be done. If passno is 2, then this |
898 | * is a revision to a previous pass which must be undone as the | | 898 | * is a revision to a previous pass which must be undone as the |
899 | * replacement pass is done. | | 899 | * replacement pass is done. |
900 | */ | | 900 | */ |
901 | static int | | 901 | static int |
902 | cgaccount1(int cg, struct vnode *vp, void *data, int passno) | | 902 | cgaccount1(int cg, struct vnode *vp, void *data, int passno) |
903 | { | | 903 | { |
904 | struct buf *bp, *ibp; | | 904 | struct buf *bp, *ibp; |
905 | struct inode *ip; | | 905 | struct inode *ip; |
906 | struct cg *cgp; | | 906 | struct cg *cgp; |
907 | struct fs *fs; | | 907 | struct fs *fs; |
908 | struct lwp *l = curlwp; | | 908 | struct lwp *l = curlwp; |
909 | daddr_t base, numblks; | | 909 | daddr_t base, numblks; |
910 | int error, len, loc, ns, indiroff; | | 910 | int error, len, loc, ns, indiroff; |
911 | | | 911 | |
912 | ip = VTOI(vp); | | 912 | ip = VTOI(vp); |
913 | fs = ip->i_fs; | | 913 | fs = ip->i_fs; |
914 | ns = UFS_FSNEEDSWAP(fs); | | 914 | ns = UFS_FSNEEDSWAP(fs); |
915 | error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), | | 915 | error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), |
916 | (int)fs->fs_cgsize, l->l_cred, 0, &bp); | | 916 | (int)fs->fs_cgsize, l->l_cred, 0, &bp); |
917 | if (error) { | | 917 | if (error) { |
918 | brelse(bp, 0); | | 918 | brelse(bp, 0); |
919 | return (error); | | 919 | return (error); |
920 | } | | 920 | } |
921 | cgp = (struct cg *)bp->b_data; | | 921 | cgp = (struct cg *)bp->b_data; |
922 | if (!cg_chkmagic(cgp, ns)) { | | 922 | if (!cg_chkmagic(cgp, ns)) { |
923 | brelse(bp, 0); | | 923 | brelse(bp, 0); |
924 | return (EIO); | | 924 | return (EIO); |
925 | } | | 925 | } |
926 | ACTIVECG_SET(fs, cg); | | 926 | ACTIVECG_SET(fs, cg); |
927 | | | 927 | |
928 | bcopy(bp->b_data, data, fs->fs_cgsize); | | 928 | bcopy(bp->b_data, data, fs->fs_cgsize); |
929 | brelse(bp, 0); | | 929 | brelse(bp, 0); |
930 | if (fs->fs_cgsize < fs->fs_bsize) | | 930 | if (fs->fs_cgsize < fs->fs_bsize) |
931 | memset((char *)data + fs->fs_cgsize, 0, | | 931 | memset((char *)data + fs->fs_cgsize, 0, |
932 | fs->fs_bsize - fs->fs_cgsize); | | 932 | fs->fs_bsize - fs->fs_cgsize); |
933 | numblks = howmany(fs->fs_size, fs->fs_frag); | | 933 | numblks = howmany(fs->fs_size, fs->fs_frag); |
934 | len = howmany(fs->fs_fpg, fs->fs_frag); | | 934 | len = howmany(fs->fs_fpg, fs->fs_frag); |
935 | base = cg * fs->fs_fpg / fs->fs_frag; | | 935 | base = cg * fs->fs_fpg / fs->fs_frag; |
936 | if (base + len >= numblks) | | 936 | if (base + len >= numblks) |
937 | len = numblks - base - 1; | | 937 | len = numblks - base - 1; |
938 | loc = 0; | | 938 | loc = 0; |
939 | if (base < NDADDR) { | | 939 | if (base < NDADDR) { |
940 | for ( ; loc < NDADDR; loc++) { | | 940 | for ( ; loc < NDADDR; loc++) { |
941 | if (ffs_isblock(fs, cg_blksfree(cgp, ns), loc)) | | 941 | if (ffs_isblock(fs, cg_blksfree(cgp, ns), loc)) |
942 | db_assign(ip, loc, BLK_NOCOPY); | | 942 | db_assign(ip, loc, BLK_NOCOPY); |
943 | else if (db_get(ip, loc) == BLK_NOCOPY) { | | 943 | else if (db_get(ip, loc) == BLK_NOCOPY) { |
944 | if (passno == 2) | | 944 | if (passno == 2) |
945 | db_assign(ip, loc, 0); | | 945 | db_assign(ip, loc, 0); |
946 | else if (passno == 1) | | 946 | else if (passno == 1) |
947 | panic("ffs_snapshot: lost direct block"); | | 947 | panic("ffs_snapshot: lost direct block"); |
948 | } | | 948 | } |
949 | } | | 949 | } |
950 | } | | 950 | } |
951 | if ((error = ffs_balloc(vp, lblktosize(fs, (off_t)(base + loc)), | | 951 | if ((error = ffs_balloc(vp, lblktosize(fs, (off_t)(base + loc)), |
952 | fs->fs_bsize, l->l_cred, B_METAONLY, &ibp)) != 0) | | 952 | fs->fs_bsize, l->l_cred, B_METAONLY, &ibp)) != 0) |
953 | return (error); | | 953 | return (error); |
954 | indiroff = (base + loc - NDADDR) % NINDIR(fs); | | 954 | indiroff = (base + loc - NDADDR) % NINDIR(fs); |
955 | for ( ; loc < len; loc++, indiroff++) { | | 955 | for ( ; loc < len; loc++, indiroff++) { |
956 | if (indiroff >= NINDIR(fs)) { | | 956 | if (indiroff >= NINDIR(fs)) { |
957 | bawrite(ibp); | | 957 | bawrite(ibp); |
958 | if ((error = ffs_balloc(vp, | | 958 | if ((error = ffs_balloc(vp, |
959 | lblktosize(fs, (off_t)(base + loc)), | | 959 | lblktosize(fs, (off_t)(base + loc)), |
960 | fs->fs_bsize, l->l_cred, B_METAONLY, &ibp)) != 0) | | 960 | fs->fs_bsize, l->l_cred, B_METAONLY, &ibp)) != 0) |
961 | return (error); | | 961 | return (error); |
962 | indiroff = 0; | | 962 | indiroff = 0; |
963 | } | | 963 | } |
964 | if (ffs_isblock(fs, cg_blksfree(cgp, ns), loc)) | | 964 | if (ffs_isblock(fs, cg_blksfree(cgp, ns), loc)) |
965 | idb_assign(ip, ibp->b_data, indiroff, BLK_NOCOPY); | | 965 | idb_assign(ip, ibp->b_data, indiroff, BLK_NOCOPY); |
966 | else if (idb_get(ip, ibp->b_data, indiroff) == BLK_NOCOPY) { | | 966 | else if (idb_get(ip, ibp->b_data, indiroff) == BLK_NOCOPY) { |
967 | if (passno == 2) | | 967 | if (passno == 2) |
968 | idb_assign(ip, ibp->b_data, indiroff, 0); | | 968 | idb_assign(ip, ibp->b_data, indiroff, 0); |
969 | else if (passno == 1) | | 969 | else if (passno == 1) |
970 | panic("ffs_snapshot: lost indirect block"); | | 970 | panic("ffs_snapshot: lost indirect block"); |
971 | } | | 971 | } |
972 | } | | 972 | } |
973 | bdwrite(ibp); | | 973 | bdwrite(ibp); |
974 | return (0); | | 974 | return (0); |
975 | } | | 975 | } |
976 | | | 976 | |
977 | /* | | 977 | /* |
978 | * Before expunging a snapshot inode, note all the | | 978 | * Before expunging a snapshot inode, note all the |
979 | * blocks that it claims with BLK_SNAP so that fsck will | | 979 | * blocks that it claims with BLK_SNAP so that fsck will |
980 | * be able to account for those blocks properly and so | | 980 | * be able to account for those blocks properly and so |
981 | * that this snapshot knows that it need not copy them | | 981 | * that this snapshot knows that it need not copy them |
982 | * if the other snapshot holding them is freed. | | 982 | * if the other snapshot holding them is freed. |
983 | */ | | 983 | */ |
984 | static int | | 984 | static int |
985 | expunge(struct vnode *snapvp, struct inode *cancelip, struct fs *fs, | | 985 | expunge(struct vnode *snapvp, struct inode *cancelip, struct fs *fs, |
986 | acctfunc_t acctfunc, int expungetype) | | 986 | acctfunc_t acctfunc, int expungetype) |
987 | { | | 987 | { |
988 | int i, error, ns; | | 988 | int i, error, ns; |
989 | daddr_t lbn, rlbn; | | 989 | daddr_t lbn, rlbn; |
990 | daddr_t len, blkno, numblks, blksperindir; | | 990 | daddr_t len, blkno, numblks, blksperindir; |
991 | struct ufs1_dinode *dip1; | | 991 | struct ufs1_dinode *dip1; |
992 | struct ufs2_dinode *dip2; | | 992 | struct ufs2_dinode *dip2; |
993 | struct lwp *l = curlwp; | | 993 | struct lwp *l = curlwp; |
994 | void *bap; | | 994 | void *bap; |
995 | struct buf *bp; | | 995 | struct buf *bp; |
996 | | | 996 | |
997 | ns = UFS_FSNEEDSWAP(fs); | | 997 | ns = UFS_FSNEEDSWAP(fs); |
998 | /* | | 998 | /* |
999 | * Prepare to expunge the inode. If its inode block has not | | 999 | * Prepare to expunge the inode. If its inode block has not |
1000 | * yet been copied, then allocate and fill the copy. | | 1000 | * yet been copied, then allocate and fill the copy. |
1001 | */ | | 1001 | */ |
1002 | lbn = fragstoblks(fs, ino_to_fsba(fs, cancelip->i_number)); | | 1002 | lbn = fragstoblks(fs, ino_to_fsba(fs, cancelip->i_number)); |
1003 | error = snapblkaddr(snapvp, lbn, &blkno); | | 1003 | error = snapblkaddr(snapvp, lbn, &blkno); |
1004 | if (error) | | 1004 | if (error) |
1005 | return error; | | 1005 | return error; |
1006 | if (blkno != 0) { | | 1006 | if (blkno != 0) { |
1007 | error = bread(snapvp, lbn, fs->fs_bsize, l->l_cred, | | 1007 | error = bread(snapvp, lbn, fs->fs_bsize, l->l_cred, |
1008 | B_MODIFY, &bp); | | 1008 | B_MODIFY, &bp); |
1009 | } else { | | 1009 | } else { |
1010 | error = ffs_balloc(snapvp, lblktosize(fs, (off_t)lbn), | | 1010 | error = ffs_balloc(snapvp, lblktosize(fs, (off_t)lbn), |
1011 | fs->fs_bsize, l->l_cred, 0, &bp); | | 1011 | fs->fs_bsize, l->l_cred, 0, &bp); |
1012 | if (! error) | | 1012 | if (! error) |
1013 | error = rwfsblk(snapvp, B_READ, bp->b_data, lbn); | | 1013 | error = rwfsblk(snapvp, B_READ, bp->b_data, lbn); |
1014 | } | | 1014 | } |
1015 | if (error) | | 1015 | if (error) |
1016 | return error; | | 1016 | return error; |
1017 | /* | | 1017 | /* |
1018 | * Set a snapshot inode to be a zero length file, regular files | | 1018 | * Set a snapshot inode to be a zero length file, regular files |
1019 | * or unlinked snapshots to be completely unallocated. | | 1019 | * or unlinked snapshots to be completely unallocated. |
1020 | */ | | 1020 | */ |
1021 | if (fs->fs_magic == FS_UFS1_MAGIC) { | | 1021 | if (fs->fs_magic == FS_UFS1_MAGIC) { |
1022 | dip1 = (struct ufs1_dinode *)bp->b_data + | | 1022 | dip1 = (struct ufs1_dinode *)bp->b_data + |
1023 | ino_to_fsbo(fs, cancelip->i_number); | | 1023 | ino_to_fsbo(fs, cancelip->i_number); |
1024 | if (expungetype == BLK_NOCOPY || cancelip->i_ffs_effnlink == 0) | | 1024 | if (expungetype == BLK_NOCOPY || cancelip->i_ffs_effnlink == 0) |
1025 | dip1->di_mode = 0; | | 1025 | dip1->di_mode = 0; |
1026 | dip1->di_size = 0; | | 1026 | dip1->di_size = 0; |
1027 | dip1->di_blocks = 0; | | 1027 | dip1->di_blocks = 0; |
1028 | dip1->di_flags = | | 1028 | dip1->di_flags = |
1029 | ufs_rw32(ufs_rw32(dip1->di_flags, ns) & ~SF_SNAPSHOT, ns); | | 1029 | ufs_rw32(ufs_rw32(dip1->di_flags, ns) & ~SF_SNAPSHOT, ns); |
1030 | bzero(&dip1->di_db[0], (NDADDR + NIADDR) * sizeof(int32_t)); | | 1030 | bzero(&dip1->di_db[0], (NDADDR + NIADDR) * sizeof(int32_t)); |
1031 | } else { | | 1031 | } else { |
1032 | dip2 = (struct ufs2_dinode *)bp->b_data + | | 1032 | dip2 = (struct ufs2_dinode *)bp->b_data + |
1033 | ino_to_fsbo(fs, cancelip->i_number); | | 1033 | ino_to_fsbo(fs, cancelip->i_number); |
1034 | if (expungetype == BLK_NOCOPY || cancelip->i_ffs_effnlink == 0) | | 1034 | if (expungetype == BLK_NOCOPY || cancelip->i_ffs_effnlink == 0) |
1035 | dip2->di_mode = 0; | | 1035 | dip2->di_mode = 0; |
1036 | dip2->di_size = 0; | | 1036 | dip2->di_size = 0; |
1037 | dip2->di_blocks = 0; | | 1037 | dip2->di_blocks = 0; |
1038 | dip2->di_flags = | | 1038 | dip2->di_flags = |
1039 | ufs_rw32(ufs_rw32(dip2->di_flags, ns) & ~SF_SNAPSHOT, ns); | | 1039 | ufs_rw32(ufs_rw32(dip2->di_flags, ns) & ~SF_SNAPSHOT, ns); |
1040 | bzero(&dip2->di_db[0], (NDADDR + NIADDR) * sizeof(int64_t)); | | 1040 | bzero(&dip2->di_db[0], (NDADDR + NIADDR) * sizeof(int64_t)); |
1041 | } | | 1041 | } |
1042 | bdwrite(bp); | | 1042 | bdwrite(bp); |
1043 | /* | | 1043 | /* |
1044 | * Now go through and expunge all the blocks in the file | | 1044 | * Now go through and expunge all the blocks in the file |
1045 | * using the function requested. | | 1045 | * using the function requested. |
1046 | */ | | 1046 | */ |
1047 | numblks = howmany(cancelip->i_size, fs->fs_bsize); | | 1047 | numblks = howmany(cancelip->i_size, fs->fs_bsize); |
1048 | if (fs->fs_magic == FS_UFS1_MAGIC) | | 1048 | if (fs->fs_magic == FS_UFS1_MAGIC) |
1049 | bap = &cancelip->i_ffs1_db[0]; | | 1049 | bap = &cancelip->i_ffs1_db[0]; |
1050 | else | | 1050 | else |
1051 | bap = &cancelip->i_ffs2_db[0]; | | 1051 | bap = &cancelip->i_ffs2_db[0]; |
1052 | if ((error = (*acctfunc)(snapvp, bap, 0, NDADDR, fs, 0, expungetype))) | | 1052 | if ((error = (*acctfunc)(snapvp, bap, 0, NDADDR, fs, 0, expungetype))) |
1053 | return (error); | | 1053 | return (error); |
1054 | if (fs->fs_magic == FS_UFS1_MAGIC) | | 1054 | if (fs->fs_magic == FS_UFS1_MAGIC) |
1055 | bap = &cancelip->i_ffs1_ib[0]; | | 1055 | bap = &cancelip->i_ffs1_ib[0]; |
1056 | else | | 1056 | else |
1057 | bap = &cancelip->i_ffs2_ib[0]; | | 1057 | bap = &cancelip->i_ffs2_ib[0]; |
1058 | if ((error = (*acctfunc)(snapvp, bap, 0, NIADDR, fs, -1, expungetype))) | | 1058 | if ((error = (*acctfunc)(snapvp, bap, 0, NIADDR, fs, -1, expungetype))) |
1059 | return (error); | | 1059 | return (error); |
1060 | blksperindir = 1; | | 1060 | blksperindir = 1; |
1061 | lbn = -NDADDR; | | 1061 | lbn = -NDADDR; |
1062 | len = numblks - NDADDR; | | 1062 | len = numblks - NDADDR; |
1063 | rlbn = NDADDR; | | 1063 | rlbn = NDADDR; |
1064 | for (i = 0; len > 0 && i < NIADDR; i++) { | | 1064 | for (i = 0; len > 0 && i < NIADDR; i++) { |
1065 | error = indiracct(snapvp, ITOV(cancelip), i, | | 1065 | error = indiracct(snapvp, ITOV(cancelip), i, |
1066 | ib_get(cancelip, i), lbn, rlbn, len, | | 1066 | ib_get(cancelip, i), lbn, rlbn, len, |
1067 | blksperindir, fs, acctfunc, expungetype); | | 1067 | blksperindir, fs, acctfunc, expungetype); |
1068 | if (error) | | 1068 | if (error) |
1069 | return (error); | | 1069 | return (error); |
1070 | blksperindir *= NINDIR(fs); | | 1070 | blksperindir *= NINDIR(fs); |
1071 | lbn -= blksperindir + 1; | | 1071 | lbn -= blksperindir + 1; |
1072 | len -= blksperindir; | | 1072 | len -= blksperindir; |
1073 | rlbn += blksperindir; | | 1073 | rlbn += blksperindir; |
1074 | } | | 1074 | } |
1075 | return (0); | | 1075 | return (0); |
1076 | } | | 1076 | } |
1077 | | | 1077 | |
1078 | /* | | 1078 | /* |
1079 | * Descend an indirect block chain for vnode cancelvp accounting for all | | 1079 | * Descend an indirect block chain for vnode cancelvp accounting for all |
1080 | * its indirect blocks in snapvp. | | 1080 | * its indirect blocks in snapvp. |
1081 | */ | | 1081 | */ |
1082 | static int | | 1082 | static int |
1083 | indiracct(struct vnode *snapvp, struct vnode *cancelvp, int level, | | 1083 | indiracct(struct vnode *snapvp, struct vnode *cancelvp, int level, |
1084 | daddr_t blkno, daddr_t lbn, daddr_t rlbn, daddr_t remblks, | | 1084 | daddr_t blkno, daddr_t lbn, daddr_t rlbn, daddr_t remblks, |
1085 | daddr_t blksperindir, struct fs *fs, acctfunc_t acctfunc, int expungetype) | | 1085 | daddr_t blksperindir, struct fs *fs, acctfunc_t acctfunc, int expungetype) |
1086 | { | | 1086 | { |
1087 | int error, num, i; | | 1087 | int error, num, i; |
1088 | daddr_t subblksperindir; | | 1088 | daddr_t subblksperindir; |
1089 | struct indir indirs[NIADDR + 2]; | | 1089 | struct indir indirs[NIADDR + 2]; |
1090 | daddr_t last; | | 1090 | daddr_t last; |
1091 | void *bap; | | 1091 | void *bap; |
1092 | struct buf *bp; | | 1092 | struct buf *bp; |
1093 | | | 1093 | |
1094 | if (blkno == 0) { | | 1094 | if (blkno == 0) { |
1095 | if (expungetype == BLK_NOCOPY) | | 1095 | if (expungetype == BLK_NOCOPY) |
1096 | return (0); | | 1096 | return (0); |
1097 | panic("indiracct: missing indir"); | | 1097 | panic("indiracct: missing indir"); |
1098 | } | | 1098 | } |
1099 | if ((error = ufs_getlbns(cancelvp, rlbn, indirs, &num)) != 0) | | 1099 | if ((error = ufs_getlbns(cancelvp, rlbn, indirs, &num)) != 0) |
1100 | return (error); | | 1100 | return (error); |
1101 | if (lbn != indirs[num - 1 - level].in_lbn || num < 2) | | 1101 | if (lbn != indirs[num - 1 - level].in_lbn || num < 2) |
1102 | panic("indiracct: botched params"); | | 1102 | panic("indiracct: botched params"); |
1103 | /* | | 1103 | /* |
1104 | * We have to expand bread here since it will deadlock looking | | 1104 | * We have to expand bread here since it will deadlock looking |
1105 | * up the block number for any blocks that are not in the cache. | | 1105 | * up the block number for any blocks that are not in the cache. |
1106 | */ | | 1106 | */ |
1107 | error = ffs_getblk(cancelvp, lbn, fsbtodb(fs, blkno), fs->fs_bsize, | | 1107 | error = ffs_getblk(cancelvp, lbn, fsbtodb(fs, blkno), fs->fs_bsize, |
1108 | false, &bp); | | 1108 | false, &bp); |
1109 | if (error) | | 1109 | if (error) |
1110 | return error; | | 1110 | return error; |
1111 | if ((bp->b_oflags & (BO_DONE | BO_DELWRI)) == 0 && (error = | | 1111 | if ((bp->b_oflags & (BO_DONE | BO_DELWRI)) == 0 && (error = |
1112 | rwfsblk(bp->b_vp, B_READ, bp->b_data, fragstoblks(fs, blkno)))) { | | 1112 | rwfsblk(bp->b_vp, B_READ, bp->b_data, fragstoblks(fs, blkno)))) { |
1113 | brelse(bp, 0); | | 1113 | brelse(bp, 0); |
1114 | return (error); | | 1114 | return (error); |
1115 | } | | 1115 | } |
1116 | /* | | 1116 | /* |
1117 | * Account for the block pointers in this indirect block. | | 1117 | * Account for the block pointers in this indirect block. |
1118 | */ | | 1118 | */ |
1119 | last = howmany(remblks, blksperindir); | | 1119 | last = howmany(remblks, blksperindir); |
1120 | if (last > NINDIR(fs)) | | 1120 | if (last > NINDIR(fs)) |
1121 | last = NINDIR(fs); | | 1121 | last = NINDIR(fs); |
1122 | bap = malloc(fs->fs_bsize, M_DEVBUF, M_WAITOK); | | 1122 | bap = malloc(fs->fs_bsize, M_DEVBUF, M_WAITOK); |
1123 | bcopy(bp->b_data, (void *)bap, fs->fs_bsize); | | 1123 | bcopy(bp->b_data, (void *)bap, fs->fs_bsize); |
1124 | brelse(bp, 0); | | 1124 | brelse(bp, 0); |
1125 | error = (*acctfunc)(snapvp, bap, 0, last, | | 1125 | error = (*acctfunc)(snapvp, bap, 0, last, |
1126 | fs, level == 0 ? rlbn : -1, expungetype); | | 1126 | fs, level == 0 ? rlbn : -1, expungetype); |
1127 | if (error || level == 0) | | 1127 | if (error || level == 0) |
1128 | goto out; | | 1128 | goto out; |
1129 | /* | | 1129 | /* |
1130 | * Account for the block pointers in each of the indirect blocks | | 1130 | * Account for the block pointers in each of the indirect blocks |
1131 | * in the levels below us. | | 1131 | * in the levels below us. |
1132 | */ | | 1132 | */ |
1133 | subblksperindir = blksperindir / NINDIR(fs); | | 1133 | subblksperindir = blksperindir / NINDIR(fs); |
1134 | for (lbn++, level--, i = 0; i < last; i++) { | | 1134 | for (lbn++, level--, i = 0; i < last; i++) { |
1135 | error = indiracct(snapvp, cancelvp, level, | | 1135 | error = indiracct(snapvp, cancelvp, level, |
1136 | idb_get(VTOI(snapvp), bap, i), lbn, rlbn, remblks, | | 1136 | idb_get(VTOI(snapvp), bap, i), lbn, rlbn, remblks, |
1137 | subblksperindir, fs, acctfunc, expungetype); | | 1137 | subblksperindir, fs, acctfunc, expungetype); |
1138 | if (error) | | 1138 | if (error) |
1139 | goto out; | | 1139 | goto out; |
1140 | rlbn += blksperindir; | | 1140 | rlbn += blksperindir; |
1141 | lbn -= blksperindir; | | 1141 | lbn -= blksperindir; |
1142 | remblks -= blksperindir; | | 1142 | remblks -= blksperindir; |
1143 | } | | 1143 | } |
1144 | out: | | 1144 | out: |
1145 | FREE(bap, M_DEVBUF); | | 1145 | FREE(bap, M_DEVBUF); |
1146 | return (error); | | 1146 | return (error); |
1147 | } | | 1147 | } |
1148 | | | 1148 | |
1149 | /* | | 1149 | /* |
1150 | * Do both snap accounting and map accounting. | | 1150 | * Do both snap accounting and map accounting. |
1151 | */ | | 1151 | */ |
1152 | static int | | 1152 | static int |
1153 | fullacct(struct vnode *vp, void *bap, int oldblkp, int lastblkp, | | 1153 | fullacct(struct vnode *vp, void *bap, int oldblkp, int lastblkp, |
1154 | struct fs *fs, daddr_t lblkno, | | 1154 | struct fs *fs, daddr_t lblkno, |
1155 | int exptype /* BLK_SNAP or BLK_NOCOPY */) | | 1155 | int exptype /* BLK_SNAP or BLK_NOCOPY */) |
1156 | { | | 1156 | { |
1157 | int error; | | 1157 | int error; |
1158 | | | 1158 | |
1159 | if ((error = snapacct(vp, bap, oldblkp, lastblkp, fs, lblkno, exptype))) | | 1159 | if ((error = snapacct(vp, bap, oldblkp, lastblkp, fs, lblkno, exptype))) |
1160 | return (error); | | 1160 | return (error); |
1161 | return (mapacct(vp, bap, oldblkp, lastblkp, fs, lblkno, exptype)); | | 1161 | return (mapacct(vp, bap, oldblkp, lastblkp, fs, lblkno, exptype)); |
1162 | } | | 1162 | } |
1163 | | | 1163 | |
1164 | /* | | 1164 | /* |
1165 | * Identify a set of blocks allocated in a snapshot inode. | | 1165 | * Identify a set of blocks allocated in a snapshot inode. |
1166 | */ | | 1166 | */ |
1167 | static int | | 1167 | static int |
1168 | snapacct(struct vnode *vp, void *bap, int oldblkp, int lastblkp, | | 1168 | snapacct(struct vnode *vp, void *bap, int oldblkp, int lastblkp, |
1169 | struct fs *fs, daddr_t lblkno, | | 1169 | struct fs *fs, daddr_t lblkno, |
1170 | int expungetype /* BLK_SNAP or BLK_NOCOPY */) | | 1170 | int expungetype /* BLK_SNAP or BLK_NOCOPY */) |
1171 | { | | 1171 | { |
1172 | struct inode *ip = VTOI(vp); | | 1172 | struct inode *ip = VTOI(vp); |
1173 | struct lwp *l = curlwp; | | 1173 | struct lwp *l = curlwp; |
1174 | daddr_t blkno; | | 1174 | daddr_t blkno; |
1175 | daddr_t lbn; | | 1175 | daddr_t lbn; |
1176 | struct buf *ibp; | | 1176 | struct buf *ibp; |
1177 | int error; | | 1177 | int error; |
1178 | | | 1178 | |
1179 | for ( ; oldblkp < lastblkp; oldblkp++) { | | 1179 | for ( ; oldblkp < lastblkp; oldblkp++) { |