1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/systm.h> 28 #include <sys/types.h> 29 #include <sys/vnode.h> 30 #include <sys/file.h> 31 #include <sys/buf.h> 32 #include <sys/ddi.h> 33 #include <sys/errno.h> 34 #include <sys/cmn_err.h> 35 #include <sys/fs/ufs_inode.h> 36 #include <sys/fs/ufs_filio.h> 37 #include <sys/fs/ufs_snap.h> 38 #include <sys/fssnap_if.h> 39 #include <sys/sysmacros.h> 40 #include <sys/modctl.h> 41 #include <sys/fs/ufs_bio.h> 42 #include <sys/debug.h> 43 #include <sys/kmem.h> 44 #include <sys/inttypes.h> 45 #include <sys/vfs.h> 46 #include <sys/disp.h> 47 #include <sys/atomic.h> 48 #include <sys/conf.h> 49 #include <sys/param.h> 50 #include <sys/policy.h> 51 52 static int ufs_snap_init_backfile(int *, int, vnode_t ***, struct ufsvfs *); 53 static void release_backing_vnodes(vnode_t ***, int); 54 static int ufs_snap_find_candidates(void *, struct ufsvfs *, int); 55 56 /* 57 * Create a snapshot on a file system 58 */ 59 int 60 ufs_snap_create(struct vnode *vp, struct fiosnapcreate_multi *fiosnapp, 61 cred_t *cr) 62 { 63 int error = 0; 64 struct ufsvfs *ufsvfsp = VTOI(vp)->i_ufsvfs; 65 struct fs *fs = ufsvfsp->vfs_fs; 66 vnode_t **bfvpp = NULL; 67 struct lockfs lf; 68 void *snapid = NULL; 69 70 u_offset_t nchunks; 71 uint_t chunksize, fragsperchunk; 72 73 /* 74 * Only privilege processes can create a snapshot for now. This 75 * would be better if it was based on the permissions of the device 76 * file. 77 */ 78 if (secpolicy_fs_config(cr, ufsvfsp->vfs_vfs) != 0) 79 return (EPERM); 80 81 /* 82 * There is no reason to make a snapshot of a read-only file system 83 */ 84 if (fs->fs_ronly) { 85 fiosnapp->error = FIOCOW_EREADONLY; 86 return (EROFS); 87 } 88 89 /* 90 * Initialize the backing files to store old data. This assumes any 91 * preallocation and setup has been done already. 92 * ufs_snap_init_backfile() allocates and returns a pointer to 93 * a null-terminated array of vnodes in bfvpp. 94 */ 95 error = ufs_snap_init_backfile(fiosnapp->backfiledesc, 96 fiosnapp->backfilecount, &bfvpp, ufsvfsp); 97 if (error) { 98 fiosnapp->error = FIOCOW_EBACKFILE; 99 return (error); 100 } 101 102 /* 103 * File system must be write locked to prevent updates while 104 * the snapshot is being established. 105 */ 106 if ((error = ufs_fiolfss(vp, &lf)) != 0) { 107 release_backing_vnodes(&bfvpp, fiosnapp->backfilecount); 108 return (error); 109 } 110 111 if (!LOCKFS_IS_ULOCK(&lf)) { 112 release_backing_vnodes(&bfvpp, fiosnapp->backfilecount); 113 fiosnapp->error = FIOCOW_EULOCK; 114 return (EINVAL); 115 } 116 117 lf.lf_lock = LOCKFS_WLOCK; 118 lf.lf_flags = 0; 119 lf.lf_comment = NULL; 120 if ((error = ufs_fiolfs(vp, &lf, 1)) != 0) { 121 release_backing_vnodes(&bfvpp, fiosnapp->backfilecount); 122 fiosnapp->error = FIOCOW_EWLOCK; 123 return (EINVAL); 124 } 125 126 /* 127 * File system must be fairly consistent to enable snapshots 128 */ 129 if (fs->fs_clean != FSACTIVE && 130 fs->fs_clean != FSSTABLE && 131 fs->fs_clean != FSCLEAN && 132 fs->fs_clean != FSLOG) { 133 fiosnapp->error = FIOCOW_ECLEAN; 134 error = EINVAL; 135 goto unlockout; 136 } 137 138 /* 139 * Only one snapshot is allowed per file system, so error if 140 * a snapshot is already enabled. 141 */ 142 if (ufsvfsp->vfs_snapshot) { 143 fiosnapp->error = FIOCOW_EBUSY; 144 error = EBUSY; 145 goto unlockout; 146 } 147 148 /* Tell bio.c how to call our strategy routine. XXX ugly hack */ 149 if (bio_snapshot_strategy == NULL) 150 bio_snapshot_strategy = 151 (void (*) (void *, buf_t *))fssnap_strategy; 152 153 /* 154 * use chunk size that is passed in, or the file system 155 * block size if it is zero. For most cases, the file system 156 * block size will be reasonably efficient. A larger 157 * chunksize uses less memory but may potentially induce more 158 * I/O copying the larger chunks aside. 159 */ 160 if (fiosnapp->chunksize != 0) 161 chunksize = fiosnapp->chunksize; 162 else 163 chunksize = fs->fs_bsize * 4; 164 165 166 /* 167 * compute the number of chunks in this whole file system. Since 168 * the UFS allocation bitmaps are in units of fragments, we first 169 * compute the number of fragments per chunk. Things work out 170 * nicer if the chunk size is a power-of-two multiple of the 171 * fragment size. 172 */ 173 if ((chunksize < fs->fs_fsize) || (chunksize % fs->fs_fsize != 0)) { 174 fiosnapp->error = FIOCOW_ECHUNKSZ; 175 error = EINVAL; 176 goto unlockout; 177 } 178 fragsperchunk = chunksize >> fs->fs_fshift; 179 nchunks = (fs->fs_size + fragsperchunk) / fragsperchunk; 180 181 /* 182 * Create and initialize snapshot state and allocate/initialize 183 * translation table. This does the real work of taking the snapshot. 184 */ 185 snapid = fssnap_create(nchunks, chunksize, fiosnapp->maxsize, vp, 186 fiosnapp->backfilecount, bfvpp, fiosnapp->backfilename, 187 fiosnapp->backfilesize); 188 if (snapid == NULL) { 189 fiosnapp->error = FIOCOW_ECREATE; 190 error = EINVAL; 191 goto unlockout; 192 } 193 194 error = ufs_snap_find_candidates(snapid, ufsvfsp, chunksize); 195 fiosnapp->snapshotnumber = fssnap_create_done(snapid); 196 197 if (error) { 198 cmn_err(CE_WARN, "ufs_snap_create: failed scanning bitmaps, " 199 "error = %d.", error); 200 fiosnapp->error = FIOCOW_EBITMAP; 201 goto unlockout; 202 } 203 204 ufsvfsp->vfs_snapshot = snapid; 205 206 unlockout: 207 /* 208 * Unlock the file system 209 */ 210 lf.lf_lock = LOCKFS_ULOCK; 211 lf.lf_flags = 0; 212 if ((ufs_fiolfs(vp, &lf, 1) != 0) && !error) { 213 fiosnapp->error = FIOCOW_ENOULOCK; 214 error = EINVAL; 215 } else { 216 fiosnapp->error = 0; 217 } 218 219 /* clean up the snapshot if an error occurred. */ 220 if (error && snapid != NULL) 221 (void) fssnap_delete(&snapid); 222 else if (error && bfvpp != NULL) 223 release_backing_vnodes(&bfvpp, fiosnapp->backfilecount); 224 225 return (error); 226 } 227 228 static int 229 ufs_snap_init_backfile(int *filedesc, int count, vnode_t ***vppp, 230 struct ufsvfs *ufsvfsp) 231 { 232 file_t *fp; 233 vnode_t **vpp; 234 int i; 235 236 vpp = (vnode_t **)kmem_zalloc((count + 1) * sizeof (vnode_t *), 237 KM_SLEEP); 238 *vppp = vpp; 239 for (i = 0; i < count; i++) { 240 if ((fp = getf(*filedesc)) == NULL) { 241 release_backing_vnodes(vppp, count); 242 *vppp = NULL; 243 return (EBADF); 244 } 245 246 ASSERT(fp->f_vnode != NULL); 247 VN_HOLD(fp->f_vnode); 248 249 *vpp = fp->f_vnode; 250 releasef(*filedesc); 251 filedesc++; 252 253 /* make sure the backing file is on a different file system */ 254 if ((*vpp)->v_vfsp == ufsvfsp->vfs_vfs) { 255 release_backing_vnodes(vppp, count); 256 *vppp = NULL; 257 return (EINVAL); 258 } 259 vpp++; 260 } 261 return (0); 262 } 263 264 static void 265 release_backing_vnodes(vnode_t ***bvppp, int count) 266 { 267 vnode_t **vpp; 268 269 vpp = *bvppp; 270 while (*vpp) { 271 VN_RELE(*vpp); 272 *vpp++ = NULL; 273 } 274 kmem_free(*bvppp, (count + 1) * sizeof (vnode_t *)); 275 *bvppp = NULL; 276 } 277 278 static int 279 ufs_snap_find_candidates(void *snapid, struct ufsvfs *ufsvfsp, int chunksize) 280 { 281 struct fs *fs = ufsvfsp->vfs_fs; 282 struct buf *cgbp; /* cylinder group buffer */ 283 struct cg *cgp; /* cylinder group data */ 284 ulong_t cg; 285 ulong_t cgbase; 286 ulong_t chunk; 287 uchar_t *blksfree; 288 289 ulong_t curfrag; 290 int error = 0; 291 292 /* 293 * read through each ufs cylinder group and fetch the fragment 294 * allocation bitmap. UFS indicates a fragment is allocated by 295 * a zero bit (not a one bit) in the fragment offset. 296 */ 297 cgbase = 0LL; 298 for (cg = 0; cg < fs->fs_ncg; cg++) { 299 /* read the cylinder group in */ 300 cgbp = BREAD(ufsvfsp->vfs_dev, 301 (daddr_t)fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize); 302 if ((error = geterror(cgbp)) != 0) { 303 brelse(cgbp); 304 goto errout; 305 } 306 cgp = cgbp->b_un.b_cg; 307 308 /* check the magic number */ 309 if (cgp->cg_magic != CG_MAGIC) { 310 cmn_err(CE_WARN, "ufs_snap_find_candidates: cg %lu " 311 "magic number (0x%x) does not match expected " 312 "magic number (0x%x)", cg, cgp->cg_magic, CG_MAGIC); 313 error = EIO; 314 goto errout; 315 } 316 317 blksfree = cg_blksfree(cgp); 318 319 /* 320 * go through the allocation bitmap and set the 321 * corresponding bit in the candidate map. 322 */ 323 for (curfrag = 0; curfrag < cgp->cg_ndblk; curfrag++) { 324 if (isclr(blksfree, curfrag)) { 325 /* 326 * this assumes chunksize is a multiple of 327 * the fragment size 328 */ 329 chunk = (ulong_t)((cgbase + curfrag) / 330 (chunksize >> fs->fs_fshift)); 331 332 fssnap_set_candidate(snapid, chunk); 333 /* 334 * no need to scan the rest of this chunk since 335 * it is already marked, so skip to the next 336 */ 337 curfrag += ((chunksize >> fs->fs_fshift) - 338 ((cgbase + curfrag) % 339 (chunksize >> fs->fs_fshift))) - 1; 340 } 341 } 342 343 cgbase += cgp->cg_ndblk; 344 ASSERT(cgbase <= fs->fs_size); 345 brelse(cgbp); 346 } /* cylinder group loop */ 347 348 ASSERT(cgbase == fs->fs_size); 349 350 errout: 351 return (error); 352 } 353 354 355 int 356 ufs_snap_delete(struct vnode *vp, struct fiosnapdelete *fiosnapp, cred_t *cr) 357 { 358 struct ufsvfs *ufsvfsp = VTOI(vp)->i_ufsvfs; 359 struct fs *fs = ufsvfsp->vfs_fs; 360 361 /* 362 * Initialize fields in the user's buffer 363 */ 364 fiosnapp->error = 0; 365 366 /* 367 * No snapshot exists, we're done. 368 */ 369 if (ufsvfsp->vfs_snapshot == NULL) 370 return (ENOENT); 371 372 /* 373 * must have sufficient privileges. 374 */ 375 if (secpolicy_fs_config(cr, ufsvfsp->vfs_vfs) != 0) 376 return (EPERM); 377 378 /* 379 * Readonly file system 380 */ 381 if (fs->fs_ronly) { 382 fiosnapp->error = FIOCOW_EREADONLY; 383 return (EROFS); 384 } 385 386 /* free the data structures and clear the vfs_snapshot field. */ 387 fiosnapp->snapshotnumber = fssnap_delete(&ufsvfsp->vfs_snapshot); 388 389 if (fiosnapp->snapshotnumber == -1) 390 return (EINVAL); 391 392 return (0); 393 } 394